refactor: reorganize modules and add Linux build tooling

This commit is contained in:
2026-08-09 12:41:08 +08:00
parent 86024fa81d
commit 5cc5fd92ed
1461 changed files with 4054 additions and 4724 deletions

View File

@@ -0,0 +1,57 @@
# 更新日志
所有对此项目的重要更改都将记录在此文件中。
此项目遵循[语义化版本控制](https://semver.org/lang/zh-CN/)。
## [未发布]
### 新增
- 升级Go版本到1.25.1,支持最新的语言特性和性能改进
- 添加完整的Dockerfile支持容器化部署
- 添加全面的Makefile支持构建、测试、部署等开发流程
- 添加docker-compose.yml支持本地开发环境
- 添加PostgreSQL和Redis依赖服务支持
- 添加Mailhog用于开发环境邮件测试
- 添加Nginx反向代理配置支持
### 改进
- 优化依赖包版本,升级到最新稳定版本
- 改进代码注释为简体中文,提高可读性
- 优化配置结构,增强错误处理能力
- 增强Docker镜像构建过程使用多阶段构建减小镜像大小
- 添加健康检查机制,提高服务可用性监控
### 变更
- Go版本从1.24.0升级到1.25.1
- 依赖包版本更新到最新稳定版本
- Docker基础镜像更新到Go 1.25
### 修复
- 修复依赖包版本冲突问题
- 优化配置文件加载逻辑
### 安全性
- 更新所有依赖包到最新版本,修复已知安全漏洞
- 添加非root用户运行容器提高安全性
- 优化Docker镜像安全配置
---
## [1.0.0] - 2024年初始版本
### 新增
- 邮件发送服务 (SMTP)
- 短信发送服务 (SMS)
- 支持阿里云和腾讯云短信服务
- gRPC和HTTP Gateway双协议支持
- 配置文件管理
- 基础的服务注册和发现
### 特性
- 支持多种邮件服务商
- 支持多种短信服务商
- 模板化消息发送
- 验证码生成和验证
- 黑名单过滤
- 发送频率限制

View File

@@ -0,0 +1,73 @@
# 使用统一的 Go 工具链作为构建环境
FROM golang:1.26.5-alpine AS builder
# 设置工作目录
WORKDIR /app
# 安装必要的工具
RUN apk add --no-cache git ca-certificates tzdata
# 设置Go环境变量
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct \
GOPRIVATE=git.apinb.com/* \
GONOPROXY=git.apinb.com/* \
GOINSECURE=git.apinb.com/* \
GONOSUMDB=git.apinb.com/* \
GOEXPERIMENT=jsonv2
# 复制go.mod和go.sum文件
COPY go.mod go.sum ./
# 下载依赖
RUN go mod download
# 复制源代码
COPY . .
# 构建应用
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags='-w -s -extldflags "-static"' \
-a -installsuffix cgo \
-o sender \
./cmd/main/main.go
# 使用最小的alpine镜像作为运行环境
FROM alpine:latest
# 安装必要的包
RUN apk --no-cache add ca-certificates tzdata
# 设置时区
ENV TZ=Asia/Shanghai
# 创建非root用户
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
# 设置工作目录
WORKDIR /app
# 从构建阶段复制二进制文件
COPY --from=builder /app/sender .
# 复制配置文件和其他必要文件
COPY --from=builder /app/etc ./etc
COPY --from=builder /app/swagger ./swagger
# 创建日志目录
RUN mkdir -p /app/logs && \
chown -R appuser:appgroup /app
# 切换到非root用户
USER appuser
# 暴露端口
EXPOSE 12201 12202
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:12202/health || exit 1
# 启动应用
CMD ["./sender"]

215
module/base/sender/Makefile Normal file
View File

@@ -0,0 +1,215 @@
# Sender Service Makefile
# 变量定义
APP_NAME := sender
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "v1.0.0")
BUILD_TIME := $(shell date +%Y-%m-%d\ %H:%M:%S)
GIT_COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
GO_VERSION := $(shell go version | awk '{print $$3}')
# 构建标志
LDFLAGS := -ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME) -X main.GitCommit=$(GIT_COMMIT) -w -s"
BUILD_FLAGS := -trimpath $(LDFLAGS)
# 目录
BUILD_DIR := build
PROTO_DIR := proto
PB_DIR := pb
# Go环境变量
export GO111MODULE=on
export GOPROXY=https://goproxy.cn,direct
export GOPRIVATE=git.apinb.com/*
export GONOPROXY=git.apinb.com/*
export GOINSECURE=git.apinb.com/*
export GONOSUMDB=git.apinb.com/*
export GOEXPERIMENT=jsonv2
.PHONY: help
help: ## 显示帮助信息
@echo "Sender Service - 可用的命令:"
@echo ""
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.PHONY: deps
deps: ## 下载依赖
@echo "下载Go依赖..."
go mod download
go mod tidy
.PHONY: proto
proto: ## 生成protobuf代码
@echo "生成protobuf代码..."
buf generate
.PHONY: build
build: deps ## 构建应用
@echo "构建应用..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME) ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)"
.PHONY: build-linux
build-linux: deps ## 构建Linux版本
@echo "构建Linux版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-linux-amd64"
.PHONY: build-windows
build-windows: deps ## 构建Windows版本
@echo "构建Windows版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe"
.PHONY: build-darwin
build-darwin: deps ## 构建macOS版本
@echo "构建macOS版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-darwin-amd64"
.PHONY: build-all
build-all: build-linux build-windows build-darwin ## 构建所有平台版本
.PHONY: run
run: build ## 运行应用
@echo "启动应用..."
./$(BUILD_DIR)/$(APP_NAME)
.PHONY: dev
dev: ## 开发模式运行
@echo "开发模式启动..."
go run ./cmd/main/main.go
.PHONY: test
test: ## 运行测试
@echo "运行测试..."
go test -v ./...
.PHONY: test-coverage
test-coverage: ## 运行测试并生成覆盖率报告
@echo "运行测试覆盖率..."
go test -v -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
@echo "覆盖率报告生成: coverage.html"
.PHONY: test-mail
test-mail: ## 运行邮件测试
@echo "运行邮件测试..."
go test -v ./test/mail_test.go
.PHONY: test-sms
test-sms: ## 运行短信测试
@echo "运行短信测试..."
go test -v ./test/sms_test.go
.PHONY: lint
lint: ## 运行代码检查
@echo "运行代码检查..."
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run; \
else \
echo "golangci-lint 未安装使用go vet..."; \
go vet ./...; \
fi
.PHONY: fmt
fmt: ## 格式化代码
@echo "格式化代码..."
go fmt ./...
@if command -v goimports >/dev/null 2>&1; then \
goimports -w .; \
fi
.PHONY: security
security: ## 安全检查
@echo "运行安全检查..."
@if command -v gosec >/dev/null 2>&1; then \
gosec ./...; \
else \
echo "gosec 未安装,请运行: go install github.com/securego/gosec/v2/cmd/gosec@latest"; \
fi
.PHONY: docker-build
docker-build: ## 构建Docker镜像
@echo "构建Docker镜像..."
docker build -t $(APP_NAME):$(VERSION) .
docker tag $(APP_NAME):$(VERSION) $(APP_NAME):latest
.PHONY: docker-run
docker-run: ## 运行Docker容器
@echo "运行Docker容器..."
docker run -d --name $(APP_NAME) -p 12201:12201 -p 12202:12202 $(APP_NAME):latest
.PHONY: docker-stop
docker-stop: ## 停止Docker容器
@echo "停止Docker容器..."
docker stop $(APP_NAME) || true
docker rm $(APP_NAME) || true
.PHONY: docker-compose-up
docker-compose-up: ## 启动docker-compose服务
@echo "启动docker-compose服务..."
docker-compose up -d
.PHONY: docker-compose-down
docker-compose-down: ## 停止docker-compose服务
@echo "停止docker-compose服务..."
docker-compose down
.PHONY: docker-compose-logs
docker-compose-logs: ## 查看docker-compose日志
docker-compose logs -f
.PHONY: clean
clean: ## 清理构建文件
@echo "清理构建文件..."
rm -rf $(BUILD_DIR)
rm -f coverage.out coverage.html
docker system prune -f
.PHONY: install-tools
install-tools: ## 安装开发工具
@echo "安装开发工具..."
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
go install github.com/bufbuild/buf/cmd/buf@latest
.PHONY: mod-update
mod-update: ## 更新依赖
@echo "更新依赖..."
go get -u ./...
go mod tidy
.PHONY: version
version: ## 显示版本信息
@echo "应用名称: $(APP_NAME)"
@echo "版本: $(VERSION)"
@echo "构建时间: $(BUILD_TIME)"
@echo "Git提交: $(GIT_COMMIT)"
@echo "Go版本: $(GO_VERSION)"
.PHONY: swagger
swagger: ## 生成Swagger文档
@echo "Swagger文档已通过protobuf生成在swagger目录"
.PHONY: init-config
init-config: ## 初始化配置文件
@echo "配置文件位于etc目录:"
@echo "- etc/sender_dev.yaml (开发环境)"
@echo "- etc/sender_test.yaml (测试环境)"
@echo "- etc/sender_prod.yaml (生产环境)"
.PHONY: backup-config
backup-config: ## 备份配置文件
@echo "备份配置文件..."
tar -czf config_backup_$(shell date +%Y%m%d_%H%M%S).tar.gz etc/
.PHONY: all
all: clean deps proto build test lint ## 执行完整的构建流程
# 默认目标
.DEFAULT_GOAL := help

View File

@@ -0,0 +1,423 @@
# Sender 消息通知微服务
一个基于 Go 语言开发的高性能消息通知微服务,提供短信、邮件发送和验证码管理功能。采用 gRPC + HTTP Gateway 架构,支持多种服务商集成。
## ✨ 功能特性
### 📱 短信服务
- **多服务商支持**:阿里云短信、腾讯云短信
- **模板管理**:支持短信模板和参数替换
- **发送限制**:基于手机号的日发送量限制
- **黑名单过滤**:支持手机号黑名单机制
- **验证码生成**:自动生成和验证短信验证码
### 📧 邮件服务
- **SMTP支持**支持标准SMTP协议发送邮件
- **模板引擎**基于Go template的邮件模板系统
- **多服务商**支持QQ邮箱、Gmail等主流邮箱服务
- **TLS加密**支持TLS/SSL安全连接
### 🔐 验证码管理
- **自动生成**支持4-10位数字验证码
- **Redis缓存**:验证码存储和过期管理
- **限流控制**:防止验证码频繁发送
- **验证接口**:提供验证码校验功能
### 🏗️ 微服务架构
- **gRPC服务**高性能RPC通信
- **HTTP Gateway**RESTful API支持
- **服务发现**基于Etcd的服务注册发现
- **健康检查**:完整的健康检查机制
## 🚀 快速开始
### 环境要求
- Go 1.26.5
- Redis 6.0+
- PostgreSQL 12+
- Etcd 3.5+
### 安装依赖
```bash
# 克隆项目
git clone bsm/full/module/base/sender.git
cd sender
# 下载依赖
make deps
```
### 配置服务
1. **复制配置文件**
```bash
cp etc/sender_dev.yaml etc/sender_local.yaml
```
2. **修改配置**
```yaml
# etc/sender_local.yaml
Service:
Name: sender
Port: "12201"
BindIP: "0.0.0.0"
# 数据库配置
Databases:
Default:
Driver: postgres
Host: localhost
Port: 5432
Database: sender_db
Username: postgres
Password: your_password
# Redis配置
Cache:
Redis:
Host: localhost
Port: 6379
Password: your_redis_password
Database: 0
# 短信服务配置
SMS:
aliyun:
Endpoint: "dysmsapi.aliyuncs.com"
AccessKeyId: "your_access_key_id"
AccessKeySecret: "your_access_key_secret"
Region: "cn-hangzhou"
# 邮件服务配置
SMTP:
qq:
Endpoint: "smtp.qq.com"
Port: 587
Username: "your_email@qq.com"
Password: "your_smtp_password"
FromAddress: "your_email@qq.com"
FromName: "系统通知"
```
### 运行服务
#### 方式一:直接运行
```bash
# 开发模式
make dev
# 生产模式
make build
make run
```
#### 方式二Docker运行
```bash
# 构建镜像
make docker-build
# 使用docker-compose启动完整环境
make docker-compose-up
```
## 📖 API文档
### gRPC接口
#### 短信服务
```protobuf
service Sms {
rpc Send(SmsSendRequest) returns (SmsReply);
rpc Verify(SmsVerifyRequest) returns (SmsReply);
}
```
#### 邮件服务
```protobuf
service Mail {
rpc Send(SendMailRequest) returns (SendMailReply);
}
```
### HTTP接口
服务启动后,可通过以下地址访问:
- **gRPC服务**`localhost:12201`
- **HTTP Gateway**`localhost:12202`
- **Swagger文档**`http://localhost:12202/swagger/`
#### 发送短信示例
```bash
curl -X POST "http://localhost:12202/v1/sms/send" \
-H "Content-Type: application/json" \
-d '{
"provider": "aliyun",
"sign_name": "您的签名",
"template_code": "SMS_123456789",
"phone": "13800138000",
"paramters": {
"code": "123456"
}
}'
```
#### 发送邮件示例
```bash
curl -X POST "http://localhost:12202/v1/mail/send" \
-H "Content-Type: application/json" \
-d '{
"provider": "qq",
"template_key": "welcome",
"to": "user@example.com",
"paramters": {
"username": "张三",
"verification_code": "123456"
}
}'
```
## 🛠️ 开发指南
### 项目结构
```
sender/
├── cmd/ # 应用程序入口
│ ├── main/ # 主服务入口
│ └── cli/ # 命令行工具
├── internal/ # 内部包
│ ├── config/ # 配置管理
│ ├── excode/ # 错误码定义
│ ├── impl/ # 依赖实现
│ ├── logic/ # 业务逻辑
│ │ ├── mail/ # 邮件服务
│ │ └── sms/ # 短信服务
│ ├── models/ # 数据模型
│ └── server/ # 服务器实现
├── pb/ # protobuf生成代码
├── proto/ # protobuf定义文件
├── etc/ # 配置文件
├── test/ # 测试文件
├── scripts/ # 脚本文件
└── swagger/ # API文档
```
### 开发命令
```bash
# 查看所有可用命令
make help
# 生成protobuf代码
make proto
# 运行测试
make test
# 代码检查
make lint
# 格式化代码
make fmt
# 构建所有平台版本
make build-all
# 运行完整构建流程
make all
```
### 添加新的短信服务商
1. **在配置中添加服务商配置**
```yaml
SMS:
new_provider:
Endpoint: "api.newprovider.com"
AccessKeyId: "your_key"
AccessKeySecret: "your_secret"
Region: "us-east-1"
```
2. **在 `internal/logic/sms/send.go` 中添加处理逻辑**
```go
case "new_provider":
if impl.Provider.NewProvider == nil {
return nil, excode.ErrProviderIsNil
}
result, err = NewProviderSender(in, smsCode)
```
3. **实现具体的发送函数**
```go
func NewProviderSender(args *pb.SmsSendRequest, code string) (map[string]any, error) {
// 实现新服务商的发送逻辑
}
```
### 添加新的邮件服务商
1. **在配置中添加SMTP配置**
```yaml
SMTP:
gmail:
Endpoint: "smtp.gmail.com"
Port: 587
Username: "your_email@gmail.com"
Password: "your_app_password"
FromAddress: "your_email@gmail.com"
FromName: "系统通知"
```
2. **在 `internal/logic/mail/send.go` 中添加处理逻辑**
```go
case "gmail":
err = GmailSender(cfg, tmpl, in.GetTo(), tplRecord.Subjet, in.GetParamters())
```
3. **实现具体的发送函数**
```go
func GmailSender(cfg *config.SmtpConf, tmpl *template.Template, to string, subject string, args map[string]string) error {
// 实现Gmail的发送逻辑
}
```
## 🧪 测试
### 运行测试
```bash
# 运行所有测试
make test
# 运行邮件测试
make test-mail
# 运行短信测试
make test-sms
# 生成测试覆盖率报告
make test-coverage
```
### 测试环境
项目提供了完整的Docker测试环境
```bash
# 启动测试环境包含Mailhog用于邮件测试
docker-compose --profile development up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f sender-service
```
## 📦 部署
### Docker部署
```bash
# 构建生产镜像
make docker-build
# 使用docker-compose部署
make docker-compose-up
# 停止服务
make docker-compose-down
```
### 生产环境配置
1. **环境变量设置**
```bash
export SERVICE_ENV=prod
export POSTGRES_PASSWORD=your_strong_password
export REDIS_PASSWORD=your_redis_password
```
2. **配置文件**
使用 `etc/sender_prod.yaml` 作为生产环境配置
3. **健康检查**
```bash
# 检查服务健康状态
curl http://localhost:12202/health
```
## 🔧 配置说明
### 主要配置项
| 配置项 | 说明 | 默认值 |
|--------|------|--------|
| `Service.Port` | 服务端口 | 12201 |
| `Service.BindIP` | 绑定IP | 0.0.0.0 |
| `Code.Length` | 验证码长度 | 6 |
| `Code.Expire` | 验证码过期时间(秒) | 300 |
| `Code.MaxSentLimit` | 每日最大发送次数 | 10 |
### 环境变量
| 环境变量 | 说明 | 示例 |
|----------|------|------|
| `SERVICE_ENV` | 服务环境 | dev/test/prod |
| `POSTGRES_PASSWORD` | 数据库密码 | your_password |
| `REDIS_PASSWORD` | Redis密码 | your_redis_password |
## 📊 监控和日志
### 健康检查
- **HTTP健康检查**`GET /health`
- **gRPC健康检查**使用gRPC健康检查协议
### 日志管理
- 日志文件位置:`/app/logs/`
- 支持结构化日志输出
- 集成ELK日志收集可选
### 性能监控
- 支持APM监控集成
- 提供Prometheus指标导出
- 支持分布式链路追踪
## 🤝 贡献指南
1. Fork 本仓库
2. 创建特性分支:`git checkout -b feature/new-feature`
3. 提交更改:`git commit -am 'Add new feature'`
4. 推送分支:`git push origin feature/new-feature`
5. 提交Pull Request
### 代码规范
- 遵循Go官方代码规范
- 使用 `gofmt` 格式化代码
- 运行 `make lint` 进行代码检查
- 确保测试覆盖率 > 80%
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 📞 支持
如有问题或建议,请通过以下方式联系:
- 提交 [Issue](https://bsm/full/module/base/sender/issues)
- 发送邮件至david.yan@qq.com
- 查看 [Wiki](https://bsm/full/module/base/sender/wiki) 获取更多文档
---
**注意**:本项目为企业内部使用,请确保在生产环境中正确配置安全参数。

View File

@@ -0,0 +1,192 @@
# Sender微服务升级总结
## 概述
本次升级参考了 `D:\work\bsm-apps\initial` 微服务的最佳实践对Sender微服务进行了全面的优化和升级。
## 主要改进
### 1. Go版本升级 ✅
- **从**: Go 1.24.0
- **到**: Go 1.25.1
- **好处**: 支持最新的语言特性和性能改进
### 2. 依赖包优化 ✅
- 更新所有依赖包到最新稳定版本
- 优化依赖结构,移除不必要的依赖
- 增强安全性,修复已知漏洞
### 3. 容器化支持 ✅
#### 新增 Dockerfile
- 使用Go 1.25官方镜像
- 多阶段构建,减小镜像大小
- 非root用户运行提高安全性
- 健康检查机制
- 优化的构建参数
#### 新增 docker-compose.yml
- 完整的本地开发环境
- PostgreSQL数据库支持
- Redis缓存支持
- Nginx反向代理可选
- Mailhog邮件测试工具开发环境
### 4. 构建和开发工具 ✅
#### 新增 Makefile
- 完整的构建流程
- 多平台编译支持Linux、Windows、macOS
- 代码检查和格式化
- 测试覆盖率报告
- Docker镜像构建
- 安全检查
- 依赖管理
### 5. 代码质量改进 ✅
#### 注释国际化
- 将所有代码注释改为简体中文
- 增加详细的函数和结构体说明
- 提高代码可读性和维护性
#### 配置优化
- 保持原有的SMS和SMTP配置结构
- 增强配置验证逻辑
- 改进错误处理
### 6. 文档完善 ✅
#### 新增 CHANGELOG.md
- 详细的版本更新记录
- 按照语义化版本控制规范
- 记录所有重要变更
## 新增文件
```
├── Dockerfile # Docker容器化配置
├── docker-compose.yml # 本地开发环境配置
├── Makefile # 构建和开发工具
├── CHANGELOG.md # 版本更新日志
└── UPGRADE_SUMMARY.md # 本升级总结文档
```
## 使用方法
### 开发环境启动
```bash
# 使用Makefile
make dev
# 或直接运行
go run ./cmd/main/main.go
```
### 构建应用
```bash
# 构建当前平台版本
make build
# 构建所有平台版本
make build-all
# 构建Docker镜像
make docker-build
```
### 本地开发环境Docker
```bash
# 启动完整开发环境
make docker-compose-up
# 仅启动应用和数据库
docker-compose up -d sender-service postgres redis
# 启动包含邮件测试工具的开发环境
docker-compose --profile development up -d
```
### 代码质量检查
```bash
# 代码格式化
make fmt
# 代码检查
make lint
# 安全检查
make security
# 运行测试
make test
# 生成测试覆盖率报告
make test-coverage
```
## 配置说明
### 环境变量
- `SERVICE_ENV`: 服务环境dev/test/prod/docker
- `POSTGRES_PASSWORD`: PostgreSQL密码
- `REDIS_PASSWORD`: Redis密码
### 端口配置
- `12201`: gRPC服务端口
- `12202`: HTTP Gateway端口
- `5432`: PostgreSQL端口
- `6379`: Redis端口
- `8025`: Mailhog Web界面端口开发环境
## 兼容性说明
### 向后兼容
- 保持所有原有API接口不变
- 配置文件结构完全兼容
- 数据库模型无变更
### 升级注意事项
1. 确保Go版本升级到1.25+
2. 运行 `go mod tidy` 更新依赖
3. 检查配置文件路径和格式
4. 更新部署脚本中的端口配置
## 性能改进
### 构建优化
- 使用Go 1.25的性能改进
- 优化Docker镜像大小
- 多阶段构建减少最终镜像体积
### 运行时优化
- 更新的依赖包提供更好的性能
- 优化的配置加载逻辑
- 改进的错误处理机制
## 安全增强
### 容器安全
- 非root用户运行
- 最小化基础镜像
- 安全的文件权限设置
### 依赖安全
- 所有依赖包更新到最新版本
- 修复已知安全漏洞
- 定期安全检查工具集成
## 下一步建议
1. **监控集成**: 考虑集成Prometheus和Grafana监控
2. **日志优化**: 结构化日志输出
3. **配置中心**: 集成配置中心支持
4. **服务网格**: 考虑Istio等服务网格集成
5. **CI/CD**: 完善持续集成和部署流程
## 总结
本次升级成功地将Sender微服务现代化提供了
- ✅ 最新的Go 1.25支持
- ✅ 完整的容器化解决方案
- ✅ 现代化的开发工具链
- ✅ 改进的代码质量和可维护性
- ✅ 增强的安全性和性能
- ✅ 完善的文档和使用指南
所有改进都保持了向后兼容性,可以安全地在生产环境中部署。

View File

@@ -0,0 +1,125 @@
package main
import (
"crypto/tls"
"fmt"
"log"
"net/smtp"
"os"
"time"
)
const (
smtpServer = "smtp.exmail.qq.com"
smtpPort = 465
maxRetries = 3
)
func main() {
log.Println("Service Cli Mode Start ...")
log.Println("Done!")
err := sendEmail(
os.Getenv("BSM_SMTP_TO"),
"Go邮件测试",
"这是一封通过Go语言发送的测试邮件",
)
if err != nil {
fmt.Printf("发送失败: %v\n", err)
} else {
fmt.Println("邮件发送成功")
}
}
func sendEmail(to, subject, body string) error {
smtpUser := os.Getenv("BSM_SMTP_USER")
smtpPassword := os.Getenv("BSM_SMTP_PASSWORD")
if smtpUser == "" || smtpPassword == "" || to == "" {
return fmt.Errorf("BSM_SMTP_USER, BSM_SMTP_PASSWORD and BSM_SMTP_TO are required")
}
// 配置SMTP认证信息需使用授权码
auth := smtp.PlainAuth(
"",
smtpUser,
smtpPassword,
smtpServer,
)
// 邮件内容构建符合RFC822标准
msg := fmt.Sprintf("To: %s\r\n"+
"From: %s\r\n"+
"Subject: %s\r\n"+
"Content-Type: text/plain; charset=UTF-8\r\n\r\n"+
"%s",
to,
smtpUser,
subject,
body,
)
// 建立TLS连接
tlsConfig := &tls.Config{
ServerName: smtpServer,
InsecureSkipVerify: false,
MinVersion: tls.VersionTLS12,
}
var lastErr error
for i := 0; i < maxRetries; i++ {
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", smtpServer, smtpPort), tlsConfig)
if err != nil {
lastErr = fmt.Errorf("TLS连接失败: %v", err)
time.Sleep(2 * time.Second)
continue
}
client, err := smtp.NewClient(conn, smtpServer)
if err != nil {
conn.Close()
lastErr = fmt.Errorf("SMTP客户端创建失败: %v", err)
continue
}
if err := client.Auth(auth); err != nil {
client.Close()
lastErr = fmt.Errorf("认证失败: %v", err)
continue
}
if err := client.Mail("yanweidong@senlinai.com"); err != nil {
client.Close()
lastErr = fmt.Errorf("MAIL命令失败: %v", err)
continue
}
if err := client.Rcpt(to); err != nil {
client.Close()
lastErr = fmt.Errorf("RCPT命令失败: %v", err)
continue
}
w, err := client.Data()
if err != nil {
client.Close()
lastErr = fmt.Errorf("DATA命令失败: %v", err)
continue
}
if _, err := w.Write([]byte(msg)); err != nil {
client.Close()
lastErr = fmt.Errorf("写入邮件内容失败: %v", err)
continue
}
if err := w.Close(); err != nil {
client.Close()
lastErr = fmt.Errorf("关闭数据流失败: %v", err)
continue
}
client.Quit()
return nil
}
return lastErr
}

View File

@@ -0,0 +1,53 @@
/**
* @作者: david.yan(david.yan@qq.com)
* @日期: 2021-11-26 15:25:03
* @描述: Sender微服务主入口程序提供邮件和短信发送功能
*/
package service
import (
"bsm/full/module/base/sender/internal/config"
"bsm/full/module/base/sender/internal/impl"
"bsm/full/module/base/sender/internal/server"
"git.apinb.com/bsm-sdk/core/service"
)
var (
// ServiceKey 服务标识符,用于配置文件加载和服务注册
ServiceKey = "sender"
)
// main 主函数启动Sender微服务
func Run() {
// 初始化配置文件
config.New(ServiceKey)
// 初始化依赖服务Redis、数据库、Etcd等
impl.NewImpl()
// 初始化gRPC和HTTP服务器
s := server.New(config.Spec.Addr)
// 创建微服务实例
srv := service.New(
s.Grpc,
&service.Options{
Addr: config.Spec.Addr, // 服务监听地址
MsConf: config.Spec.MicroService, // 微服务配置
EtcdClient: impl.EtcdService, // Etcd客户端
GatewayCtx: s.Ctx, // Gateway上下文
GatewayConf: config.Spec.Gateway, // Gateway配置
GatewayMux: s.Mux, // Gateway路由器
},
)
// 确保程序退出时优雅停止服务
defer srv.Stop()
// 启动服务并开始监听请求
srv.Start()
}
func main() {
Run()
}

View File

@@ -0,0 +1,114 @@
version: '3.8'
services:
# Sender Service
sender-service:
build:
context: .
dockerfile: Dockerfile
container_name: sender-service
restart: unless-stopped
ports:
- "12201:12201" # gRPC port
- "12202:12202" # HTTP Gateway port
environment:
- SERVICE_ENV=docker
- TZ=Asia/Shanghai
volumes:
- ./etc:/app/etc:ro
- ./logs:/app/logs
depends_on:
- postgres
- redis
networks:
- sender-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:12202/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# PostgreSQL Database
postgres:
image: postgres:15-alpine
container_name: sender-postgres
restart: unless-stopped
environment:
POSTGRES_DB: sender_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-sender_password}
TZ: Asia/Shanghai
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts:/docker-entrypoint-initdb.d:ro
ports:
- "5432:5432"
networks:
- sender-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis Cache
redis:
image: redis:7-alpine
container_name: sender-redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-sender_redis_password}
environment:
TZ: Asia/Shanghai
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- sender-network
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 3s
retries: 5
# Nginx Reverse Proxy (Optional)
nginx:
image: nginx:alpine
container_name: sender-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- sender-service
networks:
- sender-network
profiles:
- with-nginx
# Mailhog for Email Testing (Development Only)
mailhog:
image: mailhog/mailhog:latest
container_name: sender-mailhog
restart: unless-stopped
ports:
- "1025:1025" # SMTP port
- "8025:8025" # Web UI port
networks:
- sender-network
profiles:
- development
volumes:
postgres_data:
driver: local
redis_data:
driver: local
networks:
sender-network:
driver: bridge

View File

@@ -0,0 +1,66 @@
Service: sender
Port: 12208
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/
# 微服务设置
MicroService:
Enable: false
Anonymous:
- sender.Mail.Send
- sender.Sms.Send
- sender.Sms.Verify
# Gateway 设置
Gateway:
Enable: true
Port: 12207
# SMTP设置
SMTP:
qq:
Endpoint: smtp.exmail.qq.com
Port: 465
Username: yanweidong@senlinai.com
Password: CHANGE_ME
FromAddress: yanweidong@senlinai.com
FromName: David.Yan
SMS:
aliyun:
Endpoint: dysmsapi.aliyuncs.com
AccessKeyId: LTAI5tKEmKuuoixE4iw8NZbX
AccessKeySecret: CHANGE_ME
Region: cn-hangzhou
Code:
Length: 6
Expire: 300
MaxSentLimit: 5
CokeyKey: code
BlackListFilter:
- 127.0.0.1
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
# Rpc:
# fts:
# Endpoint: https://api-v2.traingo.cn/fts/v2
# SecretKey: 4ef05311358cd1c8f787281f08b38b1c
# 链路追踪,性能监控,日志收集
# APM:
# Platform: elasticAPM
# Endpoint: http://127.0.0.1:14268/api/traces

View File

@@ -0,0 +1,65 @@
Service: sender
Port: 12208
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/
# 微服务设置
MicroService:
Enable: false
Anonymous:
- sender.Mail.Send
- sender.Sms.Send
- sender.Sms.Verify
# Gateway 设置
Gateway:
Enable: true
Port: 12207
# SMTP设置
SMTP:
qq:
Endpoint: smtp.exmail.qq.com
Port: 465
Username: yanweidong@senlinai.com
Password: CHANGE_ME
FromAddress: yanweidong@senlinai.com
FromName: David.Yan
SMS:
aliyun:
Endpoint: smtp.gmail.com
AccessKeyId: <your-access-key-id>
AccessKeySecret: <your-access-key-secret>
Region: cn-hangzhou
Code:
Length: 6
Expire: 300
MaxSentLimit: 5
GenerateCode: true
CokeyKey: code
BlackListFilter:
- 127.0.0.1
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
# Rpc:
# fts:
# Endpoint: https://api-v2.traingo.cn/fts/v2
# SecretKey: 4ef05311358cd1c8f787281f08b38b1c
# 链路追踪,性能监控,日志收集
# APM:
# Platform: elasticAPM
# Endpoint: http://127.0.0.1:14268/api/traces

View File

@@ -0,0 +1,65 @@
Service: sender
Port: 12208
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/
# 微服务设置
MicroService:
Enable: false
Anonymous:
- sender.Mail.Send
- sender.Sms.Send
- sender.Sms.Verify
# Gateway 设置
Gateway:
Enable: true
Port: 12207
# SMTP设置
SMTP:
qq:
Endpoint: smtp.exmail.qq.com
Port: 465
Username: yanweidong@senlinai.com
Password: CHANGE_ME
FromAddress: yanweidong@senlinai.com
FromName: David.Yan
SMS:
aliyun:
Endpoint: smtp.gmail.com
AccessKeyId: <your-access-key-id>
AccessKeySecret: <your-access-key-secret>
Region: cn-hangzhou
Code:
Length: 6
Expire: 300
MaxSentLimit: 5
GenerateCode: true
CokeyKey: code
BlackListFilter:
- 127.0.0.1
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
# Rpc:
# fts:
# Endpoint: https://api-v2.traingo.cn/fts/v2
# SecretKey: 4ef05311358cd1c8f787281f08b38b1c
# 链路追踪,性能监控,日志收集
# APM:
# Platform: elasticAPM
# Endpoint: http://127.0.0.1:14268/api/traces

View File

@@ -0,0 +1,8 @@
[program:bsm-apps-sender]
command=/data/app/bsm-apps-sender
directory=/data/app
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/data/app/logs/apps-sender.log

88
module/base/sender/go.mod Normal file
View File

@@ -0,0 +1,88 @@
module bsm/full/module/base/sender
go 1.26.5
tool (
git.apinb.com/bsm-tools/protoc-gen-markdown
git.apinb.com/bsm-tools/protoc-gen-slc
git.apinb.com/bsm-tools/protoc-gen-ts
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
github.com/pepabo/protoc-gen-go-client
google.golang.org/grpc/cmd/protoc-gen-go-grpc
google.golang.org/protobuf/cmd/protoc-gen-go
)
require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0
google.golang.org/grpc v1.83.0
google.golang.org/protobuf v1.36.11
gorm.io/gorm v1.31.2
)
require (
filippo.io/edwards25519 v1.2.0 // indirect
git.apinb.com/bsm-tools/protoc-gen-markdown v0.0.0-20250907131213-6c1694ce6bcb // indirect
git.apinb.com/bsm-tools/protoc-gen-slc v0.0.13 // indirect
git.apinb.com/bsm-tools/protoc-gen-ts v0.0.2 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c // indirect
github.com/go-sql-driver/mysql v1.10.0 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.10.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid/v2 v2.1.2 // indirect
github.com/pepabo/protoc-gen-go-client v0.3.0 // indirect
github.com/redis/go-redis/v9 v9.22.0 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
go.etcd.io/etcd/api/v3 v3.7.1 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.7.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.28.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.6.0 // indirect
gorm.io/driver/postgres v1.6.2 // indirect
)
require (
git.apinb.com/bsm-sdk/core v0.2.0
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.7
github.com/alibabacloud-go/tea v1.3.13
github.com/aliyun/credentials-go v1.4.7
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/spf13/cast v1.10.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.39
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.1.0
go.etcd.io/etcd/client/v3 v3.7.1
)
replace git.apinb.com/bsm-sdk/core => D:/work/bsm-sdk/core

398
module/base/sender/go.sum Normal file
View File

@@ -0,0 +1,398 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
git.apinb.com/bsm-tools/protoc-gen-markdown v0.0.0-20250907131213-6c1694ce6bcb h1:GoXhkGzI5vZ9JMXCVsSRw/AGsJY0tM2aEv76YaUg2wM=
git.apinb.com/bsm-tools/protoc-gen-markdown v0.0.0-20250907131213-6c1694ce6bcb/go.mod h1:l+84zWPM4F40Ppar+jLapgjRNt6XWDsr+l0vN4Jjlm4=
git.apinb.com/bsm-tools/protoc-gen-slc v0.0.13 h1:RGg2R2kHz6IAD7scH6So2FlZYctFUDxqWR8i/+fMfOU=
git.apinb.com/bsm-tools/protoc-gen-slc v0.0.13/go.mod h1:NpJaupVAxmyBleRMAug4mGS6BQV2E32rtuiqQzj1b+A=
git.apinb.com/bsm-tools/protoc-gen-ts v0.0.2 h1:T9C1kY6iIHl5cZ7xyNgV2xlPNWxXmaWfTbD8AzpBB9M=
git.apinb.com/bsm-tools/protoc-gen-ts v0.0.2/go.mod h1:5kAPyKSSLL7HcdZvyJa+ICnOiit/O/Z963nQpGJRrao=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.9/go.mod h1:kgnXaV74AVjM3ZWJu1GhyXGuCtxljJ677oUfz6MyJOE=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12 h1:e2yCrhtWd6Qcsy4he2OL+jIAU+93Lx9OcLlPRoFLT1w=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12/go.mod h1:f2wDpbM7hK9SvLIH09zSKVU1TsyemUNOqErMscMMl7c=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.7 h1:xqn/zCWwy5T8kYz9VlmHm9dbLscT5gA4d/BjVgNufio=
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.7/go.mod h1:m/ZasERkFizmz30MYL7b3pjtltFFPju1AitpJlJ9rWc=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.10/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.12/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94=
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7MeyCxw=
github.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec=
github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pepabo/protoc-gen-go-client v0.3.0 h1:BUlzqqgMXWaEYzGXw1vqLIShP18JV7BMDm45OSLr/AU=
github.com/pepabo/protoc-gen-go-client v0.3.0/go.mod h1:NvHW7/gsmZUK2UxafqzUzWD8zEMY0lMmm+2kLVxx8Jc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0=
github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.0/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.39 h1:D7qtbjv0+L8r+Wrenk+SAsAwLVPRUkePROGWUrZY5QE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.39/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.1.0 h1:vPYUpMS+ZpBosaPvD/qFhy7m3LVEcJkUPxf90QHodwI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.1.0/go.mod h1:cVlZISIgfgYzoXidDBwgieSYYf9dtpIqVLLOCV0BqOc=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.etcd.io/etcd/api/v3 v3.7.1 h1:KJG0/DcWGfe3Y1otDf/fsBf0TSSgpxZ5RO/L8SFt73E=
go.etcd.io/etcd/api/v3 v3.7.1/go.mod h1:8bXIpCMeV7E3/XL0Ix123ATn3dB+0V7d9zklHbB0m78=
go.etcd.io/etcd/client/pkg/v3 v3.7.1 h1:rKYsj3pRkR0eK3yjT3XOgrhqfmIfj9pzNgxjh7mfFv4=
go.etcd.io/etcd/client/pkg/v3 v3.7.1/go.mod h1:cnzZGIUzSfjEwLC6UBVsSXlEK1eepS/JUD7wE6PLRT0=
go.etcd.io/etcd/client/v3 v3.7.1 h1:0PEMMC0KuZmVIN+RAbdqfkZ45pYTgKVtmBEbRCvZFUg=
go.etcd.io/etcd/client/v3 v3.7.1/go.mod h1:ffNqALa8tRCYhYo1F9oR489y23K39Gz+BSR3ApAGYq0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea h1:Jifw/kjs/r3B0uszvls/m3c3tmZs2YHGM9C+rvxP9gY=
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea h1:kVhQEPTpKQahD5+JSBTfBB19wcgQTTjAIn45MBqnyHk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/postgres v1.6.2 h1:BvXQ/cNUg63q5TFNg672DmDcowZSFrNLkkA3Xe6GXq4=
gorm.io/driver/postgres v1.6.2/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View 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)
}

View 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")
)

View 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()
}

View 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
}

View 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
}

View File

@@ -0,0 +1,8 @@
package sms
const (
FormatDay = "2006-01-02"
KeyPrefix = "/SMS/Code/"
BlackListCacheKey = "/SMS/BlackList/"
LimitCacheKey = "/SMS/LimitCacheKey/"
)

View 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()
}

View 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
}

View 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{})
}

View 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)
}

View 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
}

View 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)
}

View File

@@ -0,0 +1,217 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.8
// protoc (unknown)
// source: mail.proto
package sender
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// sms module
type SendMailRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
TemplateKey string `protobuf:"bytes,2,opt,name=template_key,json=templateKey,proto3" json:"template_key,omitempty"`
To string `protobuf:"bytes,4,opt,name=to,proto3" json:"to,omitempty"`
IsGenCode bool `protobuf:"varint,5,opt,name=is_gen_code,json=isGenCode,proto3" json:"is_gen_code,omitempty"` // 是否生成验证码
Paramters map[string]string `protobuf:"bytes,6,rep,name=paramters,proto3" json:"paramters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // 验证码相关
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendMailRequest) Reset() {
*x = SendMailRequest{}
mi := &file_mail_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendMailRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendMailRequest) ProtoMessage() {}
func (x *SendMailRequest) ProtoReflect() protoreflect.Message {
mi := &file_mail_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendMailRequest.ProtoReflect.Descriptor instead.
func (*SendMailRequest) Descriptor() ([]byte, []int) {
return file_mail_proto_rawDescGZIP(), []int{0}
}
func (x *SendMailRequest) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *SendMailRequest) GetTemplateKey() string {
if x != nil {
return x.TemplateKey
}
return ""
}
func (x *SendMailRequest) GetTo() string {
if x != nil {
return x.To
}
return ""
}
func (x *SendMailRequest) GetIsGenCode() bool {
if x != nil {
return x.IsGenCode
}
return false
}
func (x *SendMailRequest) GetParamters() map[string]string {
if x != nil {
return x.Paramters
}
return nil
}
type SendMailReply struct {
state protoimpl.MessageState `protogen:"open.v1"`
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendMailReply) Reset() {
*x = SendMailReply{}
mi := &file_mail_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendMailReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendMailReply) ProtoMessage() {}
func (x *SendMailReply) ProtoReflect() protoreflect.Message {
mi := &file_mail_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendMailReply.ProtoReflect.Descriptor instead.
func (*SendMailReply) Descriptor() ([]byte, []int) {
return file_mail_proto_rawDescGZIP(), []int{1}
}
func (x *SendMailReply) GetData() string {
if x != nil {
return x.Data
}
return ""
}
var File_mail_proto protoreflect.FileDescriptor
const file_mail_proto_rawDesc = "" +
"\n" +
"\x16" +
"base/sender/mail.proto\x12\x06sender\"\x84\x02\n" +
"\x0fSendMailRequest\x12\x1a\n" +
"\bprovider\x18\x01 \x01(\tR\bprovider\x12!\n" +
"\ftemplate_key\x18\x02 \x01(\tR\vtemplateKey\x12\x0e\n" +
"\x02to\x18\x04 \x01(\tR\x02to\x12\x1e\n" +
"\vis_gen_code\x18\x05 \x01(\bR\tisGenCode\x12D\n" +
"\tparamters\x18\x06 \x03(\v2&.sender.SendMailRequest.ParamtersEntryR\tparamters\x1a<\n" +
"\x0eParamtersEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"#\n" +
"\rSendMailReply\x12\x12\n" +
"\x04data\x18\x01 \x01(\tR\x04data2>\n" +
"\x04Mail\x126\n" +
"\x04Send\x12\x17.sender.SendMailRequest\x1a\x15.sender.SendMailReplyB\n" +
"Z\b.;senderb\x06proto3"
var (
file_mail_proto_rawDescOnce sync.Once
file_mail_proto_rawDescData []byte
)
func file_mail_proto_rawDescGZIP() []byte {
file_mail_proto_rawDescOnce.Do(func() {
file_mail_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_mail_proto_rawDesc), len(file_mail_proto_rawDesc)))
})
return file_mail_proto_rawDescData
}
var file_mail_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_mail_proto_goTypes = []any{
(*SendMailRequest)(nil), // 0: sender.SendMailRequest
(*SendMailReply)(nil), // 1: sender.SendMailReply
nil, // 2: sender.SendMailRequest.ParamtersEntry
}
var file_mail_proto_depIdxs = []int32{
2, // 0: sender.SendMailRequest.paramters:type_name -> sender.SendMailRequest.ParamtersEntry
0, // 1: sender.Mail.Send:input_type -> sender.SendMailRequest
1, // 2: sender.Mail.Send:output_type -> sender.SendMailReply
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_mail_proto_init() }
func file_mail_proto_init() {
if File_mail_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_mail_proto_rawDesc), len(file_mail_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_mail_proto_goTypes,
DependencyIndexes: file_mail_proto_depIdxs,
MessageInfos: file_mail_proto_msgTypes,
}.Build()
File_mail_proto = out.File
file_mail_proto_goTypes = nil
file_mail_proto_depIdxs = nil
}

View File

@@ -0,0 +1,157 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: mail.proto
/*
Package sender is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package sender
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
func request_Mail_Send_0(ctx context.Context, marshaler runtime.Marshaler, client MailClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SendMailRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Send(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Mail_Send_0(ctx context.Context, marshaler runtime.Marshaler, server MailServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SendMailRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Send(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMailHandlerServer registers the http handlers for service Mail to "mux".
// UnaryRPC :call MailServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMailHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterMailHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MailServer) error {
mux.Handle(http.MethodPost, pattern_Mail_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Mail/Send", runtime.WithHTTPPathPattern("/sender.Mail/Send"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Mail_Send_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Mail_Send_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterMailHandlerFromEndpoint is same as RegisterMailHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMailHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMailHandler(ctx, mux, conn)
}
// RegisterMailHandler registers the http handlers for service Mail to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMailHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMailHandlerClient(ctx, mux, NewMailClient(conn))
}
// RegisterMailHandlerClient registers the http handlers for service Mail
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MailClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MailClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MailClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterMailHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MailClient) error {
mux.Handle(http.MethodPost, pattern_Mail_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Mail/Send", runtime.WithHTTPPathPattern("/sender.Mail/Send"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Mail_Send_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Mail_Send_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Mail_Send_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Mail", "Send"}, ""))
)
var (
forward_Mail_Send_0 = runtime.ForwardResponseMessage
)

View File

@@ -0,0 +1,125 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: mail.proto
package sender
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Mail_Send_FullMethodName = "/sender.Mail/Send"
)
// MailClient is the client API for Mail service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Mail method
type MailClient interface {
Send(ctx context.Context, in *SendMailRequest, opts ...grpc.CallOption) (*SendMailReply, error)
}
type mailClient struct {
cc grpc.ClientConnInterface
}
func NewMailClient(cc grpc.ClientConnInterface) MailClient {
return &mailClient{cc}
}
func (c *mailClient) Send(ctx context.Context, in *SendMailRequest, opts ...grpc.CallOption) (*SendMailReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SendMailReply)
err := c.cc.Invoke(ctx, Mail_Send_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MailServer is the server API for Mail service.
// All implementations must embed UnimplementedMailServer
// for forward compatibility.
//
// Mail method
type MailServer interface {
Send(context.Context, *SendMailRequest) (*SendMailReply, error)
mustEmbedUnimplementedMailServer()
}
// UnimplementedMailServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMailServer struct{}
func (UnimplementedMailServer) Send(context.Context, *SendMailRequest) (*SendMailReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
}
func (UnimplementedMailServer) mustEmbedUnimplementedMailServer() {}
func (UnimplementedMailServer) testEmbeddedByValue() {}
// UnsafeMailServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MailServer will
// result in compilation errors.
type UnsafeMailServer interface {
mustEmbedUnimplementedMailServer()
}
func RegisterMailServer(s grpc.ServiceRegistrar, srv MailServer) {
// If the following call pancis, it indicates UnimplementedMailServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Mail_ServiceDesc, srv)
}
func _Mail_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendMailRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MailServer).Send(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Mail_Send_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MailServer).Send(ctx, req.(*SendMailRequest))
}
return interceptor(ctx, in, info, handler)
}
// Mail_ServiceDesc is the grpc.ServiceDesc for Mail service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Mail_ServiceDesc = grpc.ServiceDesc{
ServiceName: "sender.Mail",
HandlerType: (*MailServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Send",
Handler: _Mail_Send_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "mail.proto",
}

View File

@@ -0,0 +1,553 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.8
// protoc (unknown)
// source: push_xiaomi.proto
package sender
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type StatusReply struct {
state protoimpl.MessageState `protogen:"open.v1"`
Status int64 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` // 状态码
Identity string `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` // 标识码
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` //状态说明
Timeseq int64 `protobuf:"varint,4,opt,name=timeseq,proto3" json:"timeseq,omitempty"` // 响应时间序列
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *StatusReply) Reset() {
*x = StatusReply{}
mi := &file_push_xiaomi_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *StatusReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StatusReply) ProtoMessage() {}
func (x *StatusReply) ProtoReflect() protoreflect.Message {
mi := &file_push_xiaomi_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StatusReply.ProtoReflect.Descriptor instead.
func (*StatusReply) Descriptor() ([]byte, []int) {
return file_push_xiaomi_proto_rawDescGZIP(), []int{0}
}
func (x *StatusReply) GetStatus() int64 {
if x != nil {
return x.Status
}
return 0
}
func (x *StatusReply) GetIdentity() string {
if x != nil {
return x.Identity
}
return ""
}
func (x *StatusReply) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *StatusReply) GetTimeseq() int64 {
if x != nil {
return x.Timeseq
}
return 0
}
type BaseItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` // 消息的内容。(注意:需要对payload字符串做urlencode处理
RestrictedPackageName string `protobuf:"bytes,2,opt,name=restricted_package_name,json=restrictedPackageName,proto3" json:"restricted_package_name,omitempty"` // App的包名。备注:中间用逗号分割。
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` // 通知栏展示的通知的标题不允许全是空白字符长度小于50 一个中英文字符均计算为1通知栏消息必填
Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // 通知栏展示的通知的描述不允许全是空白字符长度小于128一个中英文字符均计算为1通知栏消息必填
TimeToLive int64 `protobuf:"varint,5,opt,name=time_to_live,json=timeToLive,proto3" json:"time_to_live,omitempty"` // 可选项。如果用户离线,设置消息在服务器保存的时间,单位:ms。服务器默认最长保留两周。
TimeToSend int64 `protobuf:"varint,6,opt,name=time_to_send,json=timeToSend,proto3" json:"time_to_send,omitempty"` // 可选项。定时发送消息。用自1970年1月1日以来00:00:00.0 UTC时间表示以毫秒为单位的时间。注:仅支持七天内的定时消息。
SoundUri string `protobuf:"bytes,7,opt,name=sound_uri,json=soundUri,proto3" json:"sound_uri,omitempty"` // 可选项自定义通知栏消息铃声url
SenderForeground string `protobuf:"bytes,8,opt,name=sender_foreground,json=senderForeground,proto3" json:"sender_foreground,omitempty"` // 可选项 '1'弹出通知栏消息(默认);'0'不会弹出通知栏消息
SenderEffect string `protobuf:"bytes,9,opt,name=sender_effect,json=senderEffect,proto3" json:"sender_effect,omitempty"` // 可选项,预定义通知栏消息的点击行为;"1":打开app的Launcher Activity。"2":打开app的任一Activity需要extra.intent_uri。"3":打开网页需要传入extra.web_uri
IntentUri string `protobuf:"bytes,10,opt,name=intent_uri,json=intentUri,proto3" json:"intent_uri,omitempty"` // 可选项打开当前app的任一组件。
WebUri string `protobuf:"bytes,11,opt,name=web_uri,json=webUri,proto3" json:"web_uri,omitempty"` // 可选项,打开某一个网页。
Jobkey string `protobuf:"bytes,12,opt,name=jobkey,proto3" json:"jobkey,omitempty"` // 可选项使用推送批次JobKey功能聚合消息。由数字[0-9]),大小写字母([a-zA-Z]下划线_和中划线-组成长度不大于20个字符
AppVersion string `protobuf:"bytes,13,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"` // 可以接收消息的app版本号用逗号分割。目前支持MiPush_SDK_Client_2_2_12_sdk.jar及以后的版本。
AppVersionNotIn string `protobuf:"bytes,14,opt,name=app_version_not_in,json=appVersionNotIn,proto3" json:"app_version_not_in,omitempty"` // 无法接收消息的app版本号用逗号分割。
Connpt string `protobuf:"bytes,15,opt,name=connpt,proto3" json:"connpt,omitempty"` // 可选项,指定在特定的网络环境下才能接收到消息。目前仅支持指定"wifi"。
OnlySendOnce string `protobuf:"bytes,16,opt,name=only_send_once,json=onlySendOnce,proto3" json:"only_send_once,omitempty"` // 可选项extra.only_send_once的值设置为'1',表示该消息仅在设备在线时发送一次,不缓存离线消息进行多次下发
SenderId int32 `protobuf:"varint,17,opt,name=sender_id,json=senderId,proto3" json:"sender_id,omitempty"` // 可选项。默认情况下通知栏只显示一条推送消息。如果通知栏要显示多条推送消息需要针对不同的消息设置不同的sender_id相同sender_id的通知栏消息会覆盖之前的且要求sender_id为取值在0~2147483647的整数。
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BaseItem) Reset() {
*x = BaseItem{}
mi := &file_push_xiaomi_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BaseItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BaseItem) ProtoMessage() {}
func (x *BaseItem) ProtoReflect() protoreflect.Message {
mi := &file_push_xiaomi_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BaseItem.ProtoReflect.Descriptor instead.
func (*BaseItem) Descriptor() ([]byte, []int) {
return file_push_xiaomi_proto_rawDescGZIP(), []int{1}
}
func (x *BaseItem) GetPayload() string {
if x != nil {
return x.Payload
}
return ""
}
func (x *BaseItem) GetRestrictedPackageName() string {
if x != nil {
return x.RestrictedPackageName
}
return ""
}
func (x *BaseItem) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *BaseItem) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *BaseItem) GetTimeToLive() int64 {
if x != nil {
return x.TimeToLive
}
return 0
}
func (x *BaseItem) GetTimeToSend() int64 {
if x != nil {
return x.TimeToSend
}
return 0
}
func (x *BaseItem) GetSoundUri() string {
if x != nil {
return x.SoundUri
}
return ""
}
func (x *BaseItem) GetSenderForeground() string {
if x != nil {
return x.SenderForeground
}
return ""
}
func (x *BaseItem) GetSenderEffect() string {
if x != nil {
return x.SenderEffect
}
return ""
}
func (x *BaseItem) GetIntentUri() string {
if x != nil {
return x.IntentUri
}
return ""
}
func (x *BaseItem) GetWebUri() string {
if x != nil {
return x.WebUri
}
return ""
}
func (x *BaseItem) GetJobkey() string {
if x != nil {
return x.Jobkey
}
return ""
}
func (x *BaseItem) GetAppVersion() string {
if x != nil {
return x.AppVersion
}
return ""
}
func (x *BaseItem) GetAppVersionNotIn() string {
if x != nil {
return x.AppVersionNotIn
}
return ""
}
func (x *BaseItem) GetConnpt() string {
if x != nil {
return x.Connpt
}
return ""
}
func (x *BaseItem) GetOnlySendOnce() string {
if x != nil {
return x.OnlySendOnce
}
return ""
}
func (x *BaseItem) GetSenderId() int32 {
if x != nil {
return x.SenderId
}
return 0
}
type ARequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
PushType string `protobuf:"bytes,1,opt,name=push_type,json=pushType,proto3" json:"push_type,omitempty"` // 推送类型:"alias";"registration_id"; 注:需要与push_id对应
PushId []string `protobuf:"bytes,2,rep,name=push_id,json=pushId,proto3" json:"push_id,omitempty"` // 根据alias或registration_id或account发送消息到指定设备上用逗号分割。
Message *BaseItem `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ARequest) Reset() {
*x = ARequest{}
mi := &file_push_xiaomi_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ARequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ARequest) ProtoMessage() {}
func (x *ARequest) ProtoReflect() protoreflect.Message {
mi := &file_push_xiaomi_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ARequest.ProtoReflect.Descriptor instead.
func (*ARequest) Descriptor() ([]byte, []int) {
return file_push_xiaomi_proto_rawDescGZIP(), []int{2}
}
func (x *ARequest) GetPushType() string {
if x != nil {
return x.PushType
}
return ""
}
func (x *ARequest) GetPushId() []string {
if x != nil {
return x.PushId
}
return nil
}
func (x *ARequest) GetMessage() *BaseItem {
if x != nil {
return x.Message
}
return nil
}
type TopicRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` // 根据topic发送消息给订阅了该topic的所有设备
Message *BaseItem `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TopicRequest) Reset() {
*x = TopicRequest{}
mi := &file_push_xiaomi_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TopicRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TopicRequest) ProtoMessage() {}
func (x *TopicRequest) ProtoReflect() protoreflect.Message {
mi := &file_push_xiaomi_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TopicRequest.ProtoReflect.Descriptor instead.
func (*TopicRequest) Descriptor() ([]byte, []int) {
return file_push_xiaomi_proto_rawDescGZIP(), []int{3}
}
func (x *TopicRequest) GetTopic() string {
if x != nil {
return x.Topic
}
return ""
}
func (x *TopicRequest) GetMessage() *BaseItem {
if x != nil {
return x.Message
}
return nil
}
type MultiTopicRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Topics string `protobuf:"bytes,1,opt,name=topics,proto3" json:"topics,omitempty"` // topic列表使用;$;分割。注: topics参数需要和topic_op参数配合使用另外topic的数量不能超过5。
TopicOp string `protobuf:"bytes,2,opt,name=topic_op,json=topicOp,proto3" json:"topic_op,omitempty"` // topic之间的操作关系。支持以下三种:UNION并集INTERSECTION交集EXCEPT差集例如:topics的列表元素是[A, B, C, D]则并集结果是ABCD交集的结果是A ∩B ∩C ∩D差集的结果是A-B-C-D。
Message *BaseItem `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MultiTopicRequest) Reset() {
*x = MultiTopicRequest{}
mi := &file_push_xiaomi_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MultiTopicRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MultiTopicRequest) ProtoMessage() {}
func (x *MultiTopicRequest) ProtoReflect() protoreflect.Message {
mi := &file_push_xiaomi_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MultiTopicRequest.ProtoReflect.Descriptor instead.
func (*MultiTopicRequest) Descriptor() ([]byte, []int) {
return file_push_xiaomi_proto_rawDescGZIP(), []int{4}
}
func (x *MultiTopicRequest) GetTopics() string {
if x != nil {
return x.Topics
}
return ""
}
func (x *MultiTopicRequest) GetTopicOp() string {
if x != nil {
return x.TopicOp
}
return ""
}
func (x *MultiTopicRequest) GetMessage() *BaseItem {
if x != nil {
return x.Message
}
return nil
}
var File_push_xiaomi_proto protoreflect.FileDescriptor
const file_push_xiaomi_proto_rawDesc = "" +
"\n" +
"\x1dbase/sender/push_xiaomi.proto\x12\x06sender\"u\n" +
"\vStatusReply\x12\x16\n" +
"\x06status\x18\x01 \x01(\x03R\x06status\x12\x1a\n" +
"\bidentity\x18\x02 \x01(\tR\bidentity\x12\x18\n" +
"\amessage\x18\x03 \x01(\tR\amessage\x12\x18\n" +
"\atimeseq\x18\x04 \x01(\x03R\atimeseq\"\xc0\x04\n" +
"\bBaseItem\x12\x18\n" +
"\apayload\x18\x01 \x01(\tR\apayload\x126\n" +
"\x17restricted_package_name\x18\x02 \x01(\tR\x15restrictedPackageName\x12\x14\n" +
"\x05title\x18\x03 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x04 \x01(\tR\vdescription\x12 \n" +
"\ftime_to_live\x18\x05 \x01(\x03R\n" +
"timeToLive\x12 \n" +
"\ftime_to_send\x18\x06 \x01(\x03R\n" +
"timeToSend\x12\x1b\n" +
"\tsound_uri\x18\a \x01(\tR\bsoundUri\x12+\n" +
"\x11sender_foreground\x18\b \x01(\tR\x10senderForeground\x12#\n" +
"\rsender_effect\x18\t \x01(\tR\fsenderEffect\x12\x1d\n" +
"\n" +
"intent_uri\x18\n" +
" \x01(\tR\tintentUri\x12\x17\n" +
"\aweb_uri\x18\v \x01(\tR\x06webUri\x12\x16\n" +
"\x06jobkey\x18\f \x01(\tR\x06jobkey\x12\x1f\n" +
"\vapp_version\x18\r \x01(\tR\n" +
"appVersion\x12+\n" +
"\x12app_version_not_in\x18\x0e \x01(\tR\x0fappVersionNotIn\x12\x16\n" +
"\x06connpt\x18\x0f \x01(\tR\x06connpt\x12$\n" +
"\x0eonly_send_once\x18\x10 \x01(\tR\fonlySendOnce\x12\x1b\n" +
"\tsender_id\x18\x11 \x01(\x05R\bsenderId\"l\n" +
"\bARequest\x12\x1b\n" +
"\tpush_type\x18\x01 \x01(\tR\bpushType\x12\x17\n" +
"\apush_id\x18\x02 \x03(\tR\x06pushId\x12*\n" +
"\amessage\x18\x03 \x01(\v2\x10.sender.BaseItemR\amessage\"P\n" +
"\fTopicRequest\x12\x14\n" +
"\x05topic\x18\x01 \x01(\tR\x05topic\x12*\n" +
"\amessage\x18\x02 \x01(\v2\x10.sender.BaseItemR\amessage\"r\n" +
"\x11MultiTopicRequest\x12\x16\n" +
"\x06topics\x18\x01 \x01(\tR\x06topics\x12\x19\n" +
"\btopic_op\x18\x02 \x01(\tR\atopicOp\x12*\n" +
"\amessage\x18\x03 \x01(\v2\x10.sender.BaseItemR\amessage2\x92\x02\n" +
"\x06Xiaomi\x120\n" +
"\x05Regid\x12\x10.sender.ARequest\x1a\x13.sender.StatusReply\"\x00\x120\n" +
"\x05Alias\x12\x10.sender.ARequest\x1a\x13.sender.StatusReply\"\x00\x124\n" +
"\x05Topic\x12\x14.sender.TopicRequest\x1a\x13.sender.StatusReply\"\x00\x12>\n" +
"\n" +
"MultiTopic\x12\x19.sender.MultiTopicRequest\x1a\x13.sender.StatusReply\"\x00\x12.\n" +
"\x03All\x12\x10.sender.BaseItem\x1a\x13.sender.StatusReply\"\x00B\n" +
"Z\b./senderb\x06proto3"
var (
file_push_xiaomi_proto_rawDescOnce sync.Once
file_push_xiaomi_proto_rawDescData []byte
)
func file_push_xiaomi_proto_rawDescGZIP() []byte {
file_push_xiaomi_proto_rawDescOnce.Do(func() {
file_push_xiaomi_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_push_xiaomi_proto_rawDesc), len(file_push_xiaomi_proto_rawDesc)))
})
return file_push_xiaomi_proto_rawDescData
}
var file_push_xiaomi_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_push_xiaomi_proto_goTypes = []any{
(*StatusReply)(nil), // 0: sender.StatusReply
(*BaseItem)(nil), // 1: sender.BaseItem
(*ARequest)(nil), // 2: sender.ARequest
(*TopicRequest)(nil), // 3: sender.TopicRequest
(*MultiTopicRequest)(nil), // 4: sender.MultiTopicRequest
}
var file_push_xiaomi_proto_depIdxs = []int32{
1, // 0: sender.ARequest.message:type_name -> sender.BaseItem
1, // 1: sender.TopicRequest.message:type_name -> sender.BaseItem
1, // 2: sender.MultiTopicRequest.message:type_name -> sender.BaseItem
2, // 3: sender.Xiaomi.Regid:input_type -> sender.ARequest
2, // 4: sender.Xiaomi.Alias:input_type -> sender.ARequest
3, // 5: sender.Xiaomi.Topic:input_type -> sender.TopicRequest
4, // 6: sender.Xiaomi.MultiTopic:input_type -> sender.MultiTopicRequest
1, // 7: sender.Xiaomi.All:input_type -> sender.BaseItem
0, // 8: sender.Xiaomi.Regid:output_type -> sender.StatusReply
0, // 9: sender.Xiaomi.Alias:output_type -> sender.StatusReply
0, // 10: sender.Xiaomi.Topic:output_type -> sender.StatusReply
0, // 11: sender.Xiaomi.MultiTopic:output_type -> sender.StatusReply
0, // 12: sender.Xiaomi.All:output_type -> sender.StatusReply
8, // [8:13] is the sub-list for method output_type
3, // [3:8] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_push_xiaomi_proto_init() }
func file_push_xiaomi_proto_init() {
if File_push_xiaomi_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_push_xiaomi_proto_rawDesc), len(file_push_xiaomi_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_push_xiaomi_proto_goTypes,
DependencyIndexes: file_push_xiaomi_proto_depIdxs,
MessageInfos: file_push_xiaomi_proto_msgTypes,
}.Build()
File_push_xiaomi_proto = out.File
file_push_xiaomi_proto_goTypes = nil
file_push_xiaomi_proto_depIdxs = nil
}

View File

@@ -0,0 +1,421 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: push_xiaomi.proto
/*
Package sender is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package sender
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
func request_Xiaomi_Regid_0(ctx context.Context, marshaler runtime.Marshaler, client XiaomiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ARequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Regid(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Xiaomi_Regid_0(ctx context.Context, marshaler runtime.Marshaler, server XiaomiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ARequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Regid(ctx, &protoReq)
return msg, metadata, err
}
func request_Xiaomi_Alias_0(ctx context.Context, marshaler runtime.Marshaler, client XiaomiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ARequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Alias(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Xiaomi_Alias_0(ctx context.Context, marshaler runtime.Marshaler, server XiaomiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ARequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Alias(ctx, &protoReq)
return msg, metadata, err
}
func request_Xiaomi_Topic_0(ctx context.Context, marshaler runtime.Marshaler, client XiaomiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq TopicRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Topic(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Xiaomi_Topic_0(ctx context.Context, marshaler runtime.Marshaler, server XiaomiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq TopicRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Topic(ctx, &protoReq)
return msg, metadata, err
}
func request_Xiaomi_MultiTopic_0(ctx context.Context, marshaler runtime.Marshaler, client XiaomiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq MultiTopicRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MultiTopic(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Xiaomi_MultiTopic_0(ctx context.Context, marshaler runtime.Marshaler, server XiaomiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq MultiTopicRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MultiTopic(ctx, &protoReq)
return msg, metadata, err
}
func request_Xiaomi_All_0(ctx context.Context, marshaler runtime.Marshaler, client XiaomiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq BaseItem
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.All(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Xiaomi_All_0(ctx context.Context, marshaler runtime.Marshaler, server XiaomiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq BaseItem
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.All(ctx, &protoReq)
return msg, metadata, err
}
// RegisterXiaomiHandlerServer registers the http handlers for service Xiaomi to "mux".
// UnaryRPC :call XiaomiServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterXiaomiHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterXiaomiHandlerServer(ctx context.Context, mux *runtime.ServeMux, server XiaomiServer) error {
mux.Handle(http.MethodPost, pattern_Xiaomi_Regid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Xiaomi/Regid", runtime.WithHTTPPathPattern("/sender.Xiaomi/Regid"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Xiaomi_Regid_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Regid_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_Alias_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Xiaomi/Alias", runtime.WithHTTPPathPattern("/sender.Xiaomi/Alias"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Xiaomi_Alias_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Alias_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_Topic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Xiaomi/Topic", runtime.WithHTTPPathPattern("/sender.Xiaomi/Topic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Xiaomi_Topic_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Topic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_MultiTopic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Xiaomi/MultiTopic", runtime.WithHTTPPathPattern("/sender.Xiaomi/MultiTopic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Xiaomi_MultiTopic_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_MultiTopic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_All_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Xiaomi/All", runtime.WithHTTPPathPattern("/sender.Xiaomi/All"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Xiaomi_All_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_All_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterXiaomiHandlerFromEndpoint is same as RegisterXiaomiHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterXiaomiHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterXiaomiHandler(ctx, mux, conn)
}
// RegisterXiaomiHandler registers the http handlers for service Xiaomi to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterXiaomiHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterXiaomiHandlerClient(ctx, mux, NewXiaomiClient(conn))
}
// RegisterXiaomiHandlerClient registers the http handlers for service Xiaomi
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "XiaomiClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "XiaomiClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "XiaomiClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterXiaomiHandlerClient(ctx context.Context, mux *runtime.ServeMux, client XiaomiClient) error {
mux.Handle(http.MethodPost, pattern_Xiaomi_Regid_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Xiaomi/Regid", runtime.WithHTTPPathPattern("/sender.Xiaomi/Regid"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Xiaomi_Regid_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Regid_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_Alias_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Xiaomi/Alias", runtime.WithHTTPPathPattern("/sender.Xiaomi/Alias"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Xiaomi_Alias_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Alias_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_Topic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Xiaomi/Topic", runtime.WithHTTPPathPattern("/sender.Xiaomi/Topic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Xiaomi_Topic_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_Topic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_MultiTopic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Xiaomi/MultiTopic", runtime.WithHTTPPathPattern("/sender.Xiaomi/MultiTopic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Xiaomi_MultiTopic_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_MultiTopic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Xiaomi_All_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Xiaomi/All", runtime.WithHTTPPathPattern("/sender.Xiaomi/All"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Xiaomi_All_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Xiaomi_All_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Xiaomi_Regid_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Xiaomi", "Regid"}, ""))
pattern_Xiaomi_Alias_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Xiaomi", "Alias"}, ""))
pattern_Xiaomi_Topic_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Xiaomi", "Topic"}, ""))
pattern_Xiaomi_MultiTopic_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Xiaomi", "MultiTopic"}, ""))
pattern_Xiaomi_All_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Xiaomi", "All"}, ""))
)
var (
forward_Xiaomi_Regid_0 = runtime.ForwardResponseMessage
forward_Xiaomi_Alias_0 = runtime.ForwardResponseMessage
forward_Xiaomi_Topic_0 = runtime.ForwardResponseMessage
forward_Xiaomi_MultiTopic_0 = runtime.ForwardResponseMessage
forward_Xiaomi_All_0 = runtime.ForwardResponseMessage
)

View File

@@ -0,0 +1,284 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: push_xiaomi.proto
package sender
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Xiaomi_Regid_FullMethodName = "/sender.Xiaomi/Regid"
Xiaomi_Alias_FullMethodName = "/sender.Xiaomi/Alias"
Xiaomi_Topic_FullMethodName = "/sender.Xiaomi/Topic"
Xiaomi_MultiTopic_FullMethodName = "/sender.Xiaomi/MultiTopic"
Xiaomi_All_FullMethodName = "/sender.Xiaomi/All"
)
// XiaomiClient is the client API for Xiaomi service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type XiaomiClient interface {
// 向一组regid列表或alias列表推送单条消息这些regId可以属于不同的包名
Regid(ctx context.Context, in *ARequest, opts ...grpc.CallOption) (*StatusReply, error)
// 向某个alias或一组alias列表推送某条消息这些alias可以属于不同的包名
Alias(ctx context.Context, in *ARequest, opts ...grpc.CallOption) (*StatusReply, error)
// 向某个topic推送单条消息
Topic(ctx context.Context, in *TopicRequest, opts ...grpc.CallOption) (*StatusReply, error)
// 向多个topic推送单条消息可以指定一个或多个包名
MultiTopic(ctx context.Context, in *MultiTopicRequest, opts ...grpc.CallOption) (*StatusReply, error)
// 向所有设备推送某条消息(可以指定一个或多个包名)
All(ctx context.Context, in *BaseItem, opts ...grpc.CallOption) (*StatusReply, error)
}
type xiaomiClient struct {
cc grpc.ClientConnInterface
}
func NewXiaomiClient(cc grpc.ClientConnInterface) XiaomiClient {
return &xiaomiClient{cc}
}
func (c *xiaomiClient) Regid(ctx context.Context, in *ARequest, opts ...grpc.CallOption) (*StatusReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusReply)
err := c.cc.Invoke(ctx, Xiaomi_Regid_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *xiaomiClient) Alias(ctx context.Context, in *ARequest, opts ...grpc.CallOption) (*StatusReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusReply)
err := c.cc.Invoke(ctx, Xiaomi_Alias_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *xiaomiClient) Topic(ctx context.Context, in *TopicRequest, opts ...grpc.CallOption) (*StatusReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusReply)
err := c.cc.Invoke(ctx, Xiaomi_Topic_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *xiaomiClient) MultiTopic(ctx context.Context, in *MultiTopicRequest, opts ...grpc.CallOption) (*StatusReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusReply)
err := c.cc.Invoke(ctx, Xiaomi_MultiTopic_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *xiaomiClient) All(ctx context.Context, in *BaseItem, opts ...grpc.CallOption) (*StatusReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusReply)
err := c.cc.Invoke(ctx, Xiaomi_All_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// XiaomiServer is the server API for Xiaomi service.
// All implementations must embed UnimplementedXiaomiServer
// for forward compatibility.
type XiaomiServer interface {
// 向一组regid列表或alias列表推送单条消息这些regId可以属于不同的包名
Regid(context.Context, *ARequest) (*StatusReply, error)
// 向某个alias或一组alias列表推送某条消息这些alias可以属于不同的包名
Alias(context.Context, *ARequest) (*StatusReply, error)
// 向某个topic推送单条消息
Topic(context.Context, *TopicRequest) (*StatusReply, error)
// 向多个topic推送单条消息可以指定一个或多个包名
MultiTopic(context.Context, *MultiTopicRequest) (*StatusReply, error)
// 向所有设备推送某条消息(可以指定一个或多个包名)
All(context.Context, *BaseItem) (*StatusReply, error)
mustEmbedUnimplementedXiaomiServer()
}
// UnimplementedXiaomiServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedXiaomiServer struct{}
func (UnimplementedXiaomiServer) Regid(context.Context, *ARequest) (*StatusReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Regid not implemented")
}
func (UnimplementedXiaomiServer) Alias(context.Context, *ARequest) (*StatusReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Alias not implemented")
}
func (UnimplementedXiaomiServer) Topic(context.Context, *TopicRequest) (*StatusReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Topic not implemented")
}
func (UnimplementedXiaomiServer) MultiTopic(context.Context, *MultiTopicRequest) (*StatusReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method MultiTopic not implemented")
}
func (UnimplementedXiaomiServer) All(context.Context, *BaseItem) (*StatusReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method All not implemented")
}
func (UnimplementedXiaomiServer) mustEmbedUnimplementedXiaomiServer() {}
func (UnimplementedXiaomiServer) testEmbeddedByValue() {}
// UnsafeXiaomiServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to XiaomiServer will
// result in compilation errors.
type UnsafeXiaomiServer interface {
mustEmbedUnimplementedXiaomiServer()
}
func RegisterXiaomiServer(s grpc.ServiceRegistrar, srv XiaomiServer) {
// If the following call pancis, it indicates UnimplementedXiaomiServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Xiaomi_ServiceDesc, srv)
}
func _Xiaomi_Regid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ARequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(XiaomiServer).Regid(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Xiaomi_Regid_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(XiaomiServer).Regid(ctx, req.(*ARequest))
}
return interceptor(ctx, in, info, handler)
}
func _Xiaomi_Alias_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ARequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(XiaomiServer).Alias(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Xiaomi_Alias_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(XiaomiServer).Alias(ctx, req.(*ARequest))
}
return interceptor(ctx, in, info, handler)
}
func _Xiaomi_Topic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TopicRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(XiaomiServer).Topic(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Xiaomi_Topic_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(XiaomiServer).Topic(ctx, req.(*TopicRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Xiaomi_MultiTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MultiTopicRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(XiaomiServer).MultiTopic(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Xiaomi_MultiTopic_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(XiaomiServer).MultiTopic(ctx, req.(*MultiTopicRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Xiaomi_All_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BaseItem)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(XiaomiServer).All(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Xiaomi_All_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(XiaomiServer).All(ctx, req.(*BaseItem))
}
return interceptor(ctx, in, info, handler)
}
// Xiaomi_ServiceDesc is the grpc.ServiceDesc for Xiaomi service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Xiaomi_ServiceDesc = grpc.ServiceDesc{
ServiceName: "sender.Xiaomi",
HandlerType: (*XiaomiServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Regid",
Handler: _Xiaomi_Regid_Handler,
},
{
MethodName: "Alias",
Handler: _Xiaomi_Alias_Handler,
},
{
MethodName: "Topic",
Handler: _Xiaomi_Topic_Handler,
},
{
MethodName: "MultiTopic",
Handler: _Xiaomi_MultiTopic_Handler,
},
{
MethodName: "All",
Handler: _Xiaomi_All_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "push_xiaomi.proto",
}

View File

@@ -0,0 +1,284 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.8
// protoc (unknown)
// source: sms.proto
package sender
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// sms module
type SmsSendRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
SignName string `protobuf:"bytes,2,opt,name=sign_name,json=signName,proto3" json:"sign_name,omitempty"` // 必传项
TemplateCode string `protobuf:"bytes,3,opt,name=template_code,json=templateCode,proto3" json:"template_code,omitempty"` // 必传项
Phone string `protobuf:"bytes,4,opt,name=phone,proto3" json:"phone,omitempty"` // 必传项
IsGenCode bool `protobuf:"varint,6,opt,name=is_gen_code,json=isGenCode,proto3" json:"is_gen_code,omitempty"` // 是否生成验证码
Paramters map[string]string `protobuf:"bytes,5,rep,name=paramters,proto3" json:"paramters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // 验证码相关
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SmsSendRequest) Reset() {
*x = SmsSendRequest{}
mi := &file_sms_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SmsSendRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SmsSendRequest) ProtoMessage() {}
func (x *SmsSendRequest) ProtoReflect() protoreflect.Message {
mi := &file_sms_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SmsSendRequest.ProtoReflect.Descriptor instead.
func (*SmsSendRequest) Descriptor() ([]byte, []int) {
return file_sms_proto_rawDescGZIP(), []int{0}
}
func (x *SmsSendRequest) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *SmsSendRequest) GetSignName() string {
if x != nil {
return x.SignName
}
return ""
}
func (x *SmsSendRequest) GetTemplateCode() string {
if x != nil {
return x.TemplateCode
}
return ""
}
func (x *SmsSendRequest) GetPhone() string {
if x != nil {
return x.Phone
}
return ""
}
func (x *SmsSendRequest) GetIsGenCode() bool {
if x != nil {
return x.IsGenCode
}
return false
}
func (x *SmsSendRequest) GetParamters() map[string]string {
if x != nil {
return x.Paramters
}
return nil
}
type SmsReply struct {
state protoimpl.MessageState `protogen:"open.v1"`
Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SmsReply) Reset() {
*x = SmsReply{}
mi := &file_sms_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SmsReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SmsReply) ProtoMessage() {}
func (x *SmsReply) ProtoReflect() protoreflect.Message {
mi := &file_sms_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SmsReply.ProtoReflect.Descriptor instead.
func (*SmsReply) Descriptor() ([]byte, []int) {
return file_sms_proto_rawDescGZIP(), []int{1}
}
func (x *SmsReply) GetReply() string {
if x != nil {
return x.Reply
}
return ""
}
type SmsVerifyRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Phone string `protobuf:"bytes,1,opt,name=phone,proto3" json:"phone,omitempty"`
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SmsVerifyRequest) Reset() {
*x = SmsVerifyRequest{}
mi := &file_sms_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SmsVerifyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SmsVerifyRequest) ProtoMessage() {}
func (x *SmsVerifyRequest) ProtoReflect() protoreflect.Message {
mi := &file_sms_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SmsVerifyRequest.ProtoReflect.Descriptor instead.
func (*SmsVerifyRequest) Descriptor() ([]byte, []int) {
return file_sms_proto_rawDescGZIP(), []int{2}
}
func (x *SmsVerifyRequest) GetPhone() string {
if x != nil {
return x.Phone
}
return ""
}
func (x *SmsVerifyRequest) GetCode() string {
if x != nil {
return x.Code
}
return ""
}
var File_sms_proto protoreflect.FileDescriptor
const file_sms_proto_rawDesc = "" +
"\n" +
"\x15base/sender/sms.proto\x12\x06sender\"\xa7\x02\n" +
"\x0eSmsSendRequest\x12\x1a\n" +
"\bprovider\x18\x01 \x01(\tR\bprovider\x12\x1b\n" +
"\tsign_name\x18\x02 \x01(\tR\bsignName\x12#\n" +
"\rtemplate_code\x18\x03 \x01(\tR\ftemplateCode\x12\x14\n" +
"\x05phone\x18\x04 \x01(\tR\x05phone\x12\x1e\n" +
"\vis_gen_code\x18\x06 \x01(\bR\tisGenCode\x12C\n" +
"\tparamters\x18\x05 \x03(\v2%.sender.SmsSendRequest.ParamtersEntryR\tparamters\x1a<\n" +
"\x0eParamtersEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\" \n" +
"\bSmsReply\x12\x14\n" +
"\x05reply\x18\x01 \x01(\tR\x05reply\"<\n" +
"\x10SmsVerifyRequest\x12\x14\n" +
"\x05phone\x18\x01 \x01(\tR\x05phone\x12\x12\n" +
"\x04code\x18\x02 \x01(\tR\x04code2m\n" +
"\x03Sms\x120\n" +
"\x04Send\x12\x16.sender.SmsSendRequest\x1a\x10.sender.SmsReply\x124\n" +
"\x06Verify\x12\x18.sender.SmsVerifyRequest\x1a\x10.sender.SmsReplyB\n" +
"Z\b.;senderb\x06proto3"
var (
file_sms_proto_rawDescOnce sync.Once
file_sms_proto_rawDescData []byte
)
func file_sms_proto_rawDescGZIP() []byte {
file_sms_proto_rawDescOnce.Do(func() {
file_sms_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_sms_proto_rawDesc), len(file_sms_proto_rawDesc)))
})
return file_sms_proto_rawDescData
}
var file_sms_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_sms_proto_goTypes = []any{
(*SmsSendRequest)(nil), // 0: sender.SmsSendRequest
(*SmsReply)(nil), // 1: sender.SmsReply
(*SmsVerifyRequest)(nil), // 2: sender.SmsVerifyRequest
nil, // 3: sender.SmsSendRequest.ParamtersEntry
}
var file_sms_proto_depIdxs = []int32{
3, // 0: sender.SmsSendRequest.paramters:type_name -> sender.SmsSendRequest.ParamtersEntry
0, // 1: sender.Sms.Send:input_type -> sender.SmsSendRequest
2, // 2: sender.Sms.Verify:input_type -> sender.SmsVerifyRequest
1, // 3: sender.Sms.Send:output_type -> sender.SmsReply
1, // 4: sender.Sms.Verify:output_type -> sender.SmsReply
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_sms_proto_init() }
func file_sms_proto_init() {
if File_sms_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_sms_proto_rawDesc), len(file_sms_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_sms_proto_goTypes,
DependencyIndexes: file_sms_proto_depIdxs,
MessageInfos: file_sms_proto_msgTypes,
}.Build()
File_sms_proto = out.File
file_sms_proto_goTypes = nil
file_sms_proto_depIdxs = nil
}

View File

@@ -0,0 +1,223 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: sms.proto
/*
Package sender is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package sender
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
func request_Sms_Send_0(ctx context.Context, marshaler runtime.Marshaler, client SmsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SmsSendRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Send(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Sms_Send_0(ctx context.Context, marshaler runtime.Marshaler, server SmsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SmsSendRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Send(ctx, &protoReq)
return msg, metadata, err
}
func request_Sms_Verify_0(ctx context.Context, marshaler runtime.Marshaler, client SmsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SmsVerifyRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Verify(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Sms_Verify_0(ctx context.Context, marshaler runtime.Marshaler, server SmsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SmsVerifyRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Verify(ctx, &protoReq)
return msg, metadata, err
}
// RegisterSmsHandlerServer registers the http handlers for service Sms to "mux".
// UnaryRPC :call SmsServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterSmsHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterSmsHandlerServer(ctx context.Context, mux *runtime.ServeMux, server SmsServer) error {
mux.Handle(http.MethodPost, pattern_Sms_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Sms/Send", runtime.WithHTTPPathPattern("/sender.Sms/Send"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Sms_Send_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Sms_Send_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Sms_Verify_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/sender.Sms/Verify", runtime.WithHTTPPathPattern("/sender.Sms/Verify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Sms_Verify_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Sms_Verify_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterSmsHandlerFromEndpoint is same as RegisterSmsHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterSmsHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterSmsHandler(ctx, mux, conn)
}
// RegisterSmsHandler registers the http handlers for service Sms to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterSmsHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterSmsHandlerClient(ctx, mux, NewSmsClient(conn))
}
// RegisterSmsHandlerClient registers the http handlers for service Sms
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "SmsClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "SmsClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "SmsClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterSmsHandlerClient(ctx context.Context, mux *runtime.ServeMux, client SmsClient) error {
mux.Handle(http.MethodPost, pattern_Sms_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Sms/Send", runtime.WithHTTPPathPattern("/sender.Sms/Send"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Sms_Send_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Sms_Send_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Sms_Verify_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/sender.Sms/Verify", runtime.WithHTTPPathPattern("/sender.Sms/Verify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Sms_Verify_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Sms_Verify_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Sms_Send_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Sms", "Send"}, ""))
pattern_Sms_Verify_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"sender.Sms", "Verify"}, ""))
)
var (
forward_Sms_Send_0 = runtime.ForwardResponseMessage
forward_Sms_Verify_0 = runtime.ForwardResponseMessage
)

View File

@@ -0,0 +1,163 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: sms.proto
package sender
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Sms_Send_FullMethodName = "/sender.Sms/Send"
Sms_Verify_FullMethodName = "/sender.Sms/Verify"
)
// SmsClient is the client API for Sms service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// sms method
type SmsClient interface {
Send(ctx context.Context, in *SmsSendRequest, opts ...grpc.CallOption) (*SmsReply, error)
Verify(ctx context.Context, in *SmsVerifyRequest, opts ...grpc.CallOption) (*SmsReply, error)
}
type smsClient struct {
cc grpc.ClientConnInterface
}
func NewSmsClient(cc grpc.ClientConnInterface) SmsClient {
return &smsClient{cc}
}
func (c *smsClient) Send(ctx context.Context, in *SmsSendRequest, opts ...grpc.CallOption) (*SmsReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SmsReply)
err := c.cc.Invoke(ctx, Sms_Send_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *smsClient) Verify(ctx context.Context, in *SmsVerifyRequest, opts ...grpc.CallOption) (*SmsReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SmsReply)
err := c.cc.Invoke(ctx, Sms_Verify_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// SmsServer is the server API for Sms service.
// All implementations must embed UnimplementedSmsServer
// for forward compatibility.
//
// sms method
type SmsServer interface {
Send(context.Context, *SmsSendRequest) (*SmsReply, error)
Verify(context.Context, *SmsVerifyRequest) (*SmsReply, error)
mustEmbedUnimplementedSmsServer()
}
// UnimplementedSmsServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedSmsServer struct{}
func (UnimplementedSmsServer) Send(context.Context, *SmsSendRequest) (*SmsReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
}
func (UnimplementedSmsServer) Verify(context.Context, *SmsVerifyRequest) (*SmsReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Verify not implemented")
}
func (UnimplementedSmsServer) mustEmbedUnimplementedSmsServer() {}
func (UnimplementedSmsServer) testEmbeddedByValue() {}
// UnsafeSmsServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to SmsServer will
// result in compilation errors.
type UnsafeSmsServer interface {
mustEmbedUnimplementedSmsServer()
}
func RegisterSmsServer(s grpc.ServiceRegistrar, srv SmsServer) {
// If the following call pancis, it indicates UnimplementedSmsServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Sms_ServiceDesc, srv)
}
func _Sms_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SmsSendRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SmsServer).Send(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Sms_Send_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SmsServer).Send(ctx, req.(*SmsSendRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Sms_Verify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SmsVerifyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SmsServer).Verify(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Sms_Verify_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SmsServer).Verify(ctx, req.(*SmsVerifyRequest))
}
return interceptor(ctx, in, info, handler)
}
// Sms_ServiceDesc is the grpc.ServiceDesc for Sms service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Sms_ServiceDesc = grpc.ServiceDesc{
ServiceName: "sender.Sms",
HandlerType: (*SmsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Send",
Handler: _Sms_Send_Handler,
},
{
MethodName: "Verify",
Handler: _Sms_Verify_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "sms.proto",
}

View File

@@ -0,0 +1,21 @@
syntax = "proto3";
package sender;
option go_package=".;sender";
// Mail method
service Mail {
rpc Send(SendMailRequest) returns (SendMailReply);
}
// sms module
message SendMailRequest {
string provider =1;
string template_key = 2;
string to = 4;
bool is_gen_code = 5; // 是否生成验证码
map<string,string> paramters=6; // 验证码相关
}
message SendMailReply {
string data = 1;
}

View File

@@ -0,0 +1,28 @@
syntax = "proto3";
package sender ;
option go_package=".;sender";
// sms method
service Sms {
rpc Send(SmsSendRequest) returns (SmsReply);
rpc Verify(SmsVerifyRequest) returns (SmsReply);
}
// sms module
message SmsSendRequest {
string provider =1;
string sign_name = 2; // 必传项
string template_code = 3; // 必传项
string phone = 4; // 必传项
bool is_gen_code = 6; // 是否生成验证码
map<string,string> paramters=5; // 验证码相关
}
message SmsReply {
string reply = 1;
}
message SmsVerifyRequest {
string phone = 1;
string code = 2;
}

View File

@@ -0,0 +1,32 @@
filebeat.inputs:
- type: log
enabled: true
# 开启json解析
json.keys_under_root: true
json.add_error_key: true
# 日志文件路径
paths:
- ./logs/sender/error.log
- ./logs/sender/slow.log
setup.template.settings:
index.number_of_shards: 1
# 定义kafka topic field
fields:
log_topic: scf.sender.dev
# 输出到kafka
output.kafka:
hosts: ["127.0.0.1:9092"]
topic: '%{[fields.log_topic]}'
partition.round_robin:
reachable_only: false
required_acks: 1
keep_alive: 10s
# ================================= Processors =================================
processors:
- decode_json_fields:
fields: ['@timestamp','level','content','trace','span','duration']
target: ""

View File

@@ -0,0 +1,5 @@
#install
go install github.com/securego/gosec/v2/cmd/gosec@latest
#run
gosec -fmt=json -out=./test/lint/gosec.json ./...

View File

@@ -0,0 +1,68 @@
{
"swagger": "2.0",
"info": {
"title": "mail.proto",
"version": "version not set"
},
"tags": [
{
"name": "Mail"
},
{
"name": "Sms"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {},
"definitions": {
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
},
"senderSendMailReply": {
"type": "object",
"properties": {
"data": {
"type": "string"
}
}
},
"senderSmsReply": {
"type": "object",
"properties": {
"reply": {
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
//go:build integration
package test
import (
"context"
"log"
"testing"
"time"
pb "bsm/full/module/base/sender/pb"
"git.apinb.com/bsm-sdk/core/utils"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func TestMail(t *testing.T) {
conn, err := grpc.NewClient("api.apinb.com:10020",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("连接失败: %v", err)
}
defer conn.Close()
client := pb.NewMailClient(conn)
ctx, cancel := createContext()
defer cancel()
req := &pb.SendMailRequest{
TemplateKey: "default",
To: "271055687@qq.com",
Paramters: map[string]string{
"code": "123456",
},
Provider: "gmail",
}
res, err := client.Send(ctx, req)
if err != nil {
log.Fatalf("RPC调用失败: %v", err)
}
log.Printf("响应结果: %v", res.String())
}
// 创建上下文和元数据
func createContext() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
newMetaData := metadata.New(nil)
newMetaData.Set("request_id", utils.UUID())
outCtx := metadata.NewOutgoingContext(ctx, newMetaData)
return outCtx, cancel
}

View File

@@ -0,0 +1,44 @@
//go:build integration
package test
import (
"context"
"log"
"testing"
"time"
pb "bsm/full/module/base/sender/pb"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func TestSms(t *testing.T) {
conn, err := grpc.NewClient("192.168.31.148:12208",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("连接失败: %v", err)
}
defer conn.Close()
client := pb.NewSmsClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req := &pb.SmsSendRequest{
Phone: "18610192292",
TemplateCode: "SMS_101025114",
Paramters: map[string]string{
"code": "123456",
},
SignName: "身份验证",
Provider: "aliyun",
}
res, err := client.Send(ctx, req)
if err != nil {
log.Fatalf("RPC调用失败: %v", err)
}
log.Printf("响应结果: %v", res.GetReply())
}

View File

@@ -0,0 +1,12 @@
POST http://api.apinb.com/sender.Mail/Send
content-type: application/json
{
"provider": "qq",
"template_key": "verify_code",
"to": "2481286@qq.com",
"is_gen_code": true,
"paramters": {
"product": "深圳市泰达维科技"
}
}

View File

@@ -0,0 +1,13 @@
POST http://api.apinb.com/sender.Sms/Send
content-type: application/json
{
"provider": "aliyun",
"sign_name": "深圳市泰达维科技",
"template_code": "SMS_69155561",
"phone": "18610192292",
"is_gen_code": true,
"paramters": {
"product": "深圳市泰达维科技"
}
}

View File

@@ -0,0 +1,7 @@
POST http://api.apinb.com/sender.Sms/Verify
content-type: application/json
{
"phone": "18610192292",
"code": "123456"
}