sync: 同步短信服务 v1.0.4 并脱敏
This commit is contained in:
@@ -1,29 +1,79 @@
|
||||
# iotlcm-sms
|
||||
|
||||
独立短信发送服务,HTTP + gRPC 双协议共用同一套实现,支持**阿里云**与**腾讯云**短信。
|
||||
独立短信 HTTP/gRPC 服务,支持阿里云和腾讯云短信。
|
||||
|
||||
服务自身可持有全局供应商凭证与签名,调用方只需传模板 ID 和短信内容;也可以在每次请求里显式传入完整凭证,由调用方自行管理。
|
||||
|
||||
## 特性
|
||||
|
||||
- **双协议**:`POST /api/v1/sms/send`(HTTP)与 `PbNotice.Notice/Send`(gRPC)共用 `internal/sms` 实现
|
||||
- **双供应商**:`serviceType=1` 阿里云、`serviceType=2` 腾讯云
|
||||
- **配置三级合并**:`config.yaml` 默认值 → `SMS_*` 环境变量覆盖 → 调用方请求显式字段优先
|
||||
- **凭证不落库**:真实凭证只放在服务器上的 `config.yaml`(只读挂载),仓库内只保留 `config.example.yaml`
|
||||
- **健康检查**:`GET /healthz`
|
||||
- **gRPC reflection**:可直接用 `grpcurl` 查看线上接口
|
||||
|
||||
## 快速开始
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `:8080`,gRPC 默认监听 `:11200`:
|
||||
默认监听 `:8080`,可通过 `HTTP_ADDR` 修改:
|
||||
|
||||
```bash
|
||||
HTTP_ADDR=:9090 GRPC_ADDR=:11201 go run .
|
||||
HTTP_ADDR=:9090 go run .
|
||||
```
|
||||
|
||||
gRPC 默认监听 `:11200`,可通过 `GRPC_ADDR` 修改。HTTP 与 gRPC 使用同一套短信发送实现。
|
||||
|
||||
## 默认供应商配置
|
||||
|
||||
短信服务自身持有全局凭证与签名,调用方只需传模板 ID 和短信内容。默认配置按下面的顺序合并:
|
||||
|
||||
```text
|
||||
config.yaml 默认值 → SMS_* 环境变量覆盖 → 调用方请求中显式提供的字段优先
|
||||
```
|
||||
|
||||
配置文件查找顺序:`SMS_CONFIG_FILE` 指定路径 → `./config.yaml` → `./config/config.yaml` → `/app/config/config.yaml`。
|
||||
|
||||
```yaml
|
||||
# config.yaml(真实凭证只放服务器,不要提交 Git)
|
||||
serviceType: "1" # 1=阿里云,2=腾讯云
|
||||
regionId: cn-hangzhou
|
||||
accessKeyId: ""
|
||||
accessKeySecret: ""
|
||||
signName: "示例企业"
|
||||
```
|
||||
|
||||
可用环境变量覆盖:`SMS_SERVICE_TYPE`、`SMS_REGION_ID`、`SMS_ACCESS_KEY_ID`、
|
||||
`SMS_ACCESS_KEY_SECRET`、`SMS_SDK_APP_ID`、`SMS_SIGN_NAME`、`SMS_TEMPLATE_ID`。
|
||||
|
||||
仓库内提供 `config.example.yaml` 作为模板;`.gitignore` 已排除真实 `config.yaml`。
|
||||
启动日志会打印实际加载的配置来源(`file=... env=...`),便于排查。
|
||||
|
||||
## 场景模板配置库(可选)
|
||||
|
||||
除全局凭证外,还支持按业务场景(scene)从数据库回填模板与签名:调用方只传 `scene`
|
||||
和内容参数,无需关心具体模板 ID。连接参数与凭证同放 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
# 场景模板配置库(表 t_sms_template_config);DSN 留空或省略 = 关闭该能力
|
||||
sceneDbDsn: "user:pass@tcp(127.0.0.1:3306)/db_iotlcm?charset=utf8mb4&parseTime=true&loc=Local"
|
||||
sceneDbCacheTtl: 60s # 场景配置内存缓存时长,"改库即生效"的最长延迟
|
||||
```
|
||||
|
||||
可用环境变量覆盖:`SMS_DB_DSN`、`SMS_DB_CONFIG_CACHE_TTL`。DSN 未配置时,
|
||||
带 `scene` 的请求返回 400(`短信场景未配置或已停用`)。
|
||||
|
||||
生产环境使用 `deploy/docker-compose.yml` 容器化部署,HTTP 端口为 `11190`,
|
||||
gRPC 端口为 `11200`。
|
||||
|
||||
```bash
|
||||
docker build --platform linux/amd64 -f build/Dockerfile \
|
||||
-t <your-registry>/iotlcm-sms:latest .
|
||||
docker push <your-registry>/iotlcm-sms:latest
|
||||
|
||||
cd deploy
|
||||
# 先在服务器放置真实 config.yaml(不入库)
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
生产部署建议通过 `IOTLCM_SMS_IMAGE` 指定不可变版本标签:
|
||||
|
||||
```bash
|
||||
IOTLCM_SMS_IMAGE=<your-registry>/iotlcm-sms:<commit> docker compose up -d
|
||||
```
|
||||
|
||||
健康检查:
|
||||
@@ -32,43 +82,11 @@ HTTP_ADDR=:9090 GRPC_ADDR=:11201 go run .
|
||||
curl http://127.0.0.1:8080/healthz
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### 配置文件
|
||||
|
||||
查找顺序:`SMS_CONFIG_FILE` 指定路径 → `./config.yaml` → `./config/config.yaml` → `/app/config/config.yaml`。
|
||||
|
||||
```yaml
|
||||
# config.yaml(真实凭证只放服务器,不要提交 Git)
|
||||
serviceType: "1" # 1=阿里云,2=腾讯云
|
||||
regionId: cn-hangzhou
|
||||
accessKeyId: ""
|
||||
accessKeySecret: ""
|
||||
signName: "示例签名"
|
||||
# templateId: "" # 模板 ID 由调用方按业务场景传入
|
||||
# sdkAppId: "" # 仅腾讯云需要
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
| 变量 | 作用 |
|
||||
|---|---|
|
||||
| `SMS_SERVICE_TYPE` | 1=阿里云,2=腾讯云 |
|
||||
| `SMS_REGION_ID` | 区域 ID,默认 `cn-hangzhou` |
|
||||
| `SMS_ACCESS_KEY_ID` | AccessKey ID |
|
||||
| `SMS_ACCESS_KEY_SECRET` | AccessKey Secret |
|
||||
| `SMS_SDK_APP_ID` | 腾讯云 SDKAppID |
|
||||
| `SMS_SIGN_NAME` | 短信签名 |
|
||||
| `SMS_TEMPLATE_ID` | 默认模板 ID(通常由调用方按场景传入) |
|
||||
| `SMS_CONFIG_FILE` | 指定配置文件路径 |
|
||||
| `HTTP_ADDR` | HTTP 监听地址,默认 `:8080` |
|
||||
| `GRPC_ADDR` | gRPC 监听地址,默认 `:11200` |
|
||||
|
||||
启动日志会打印实际加载的配置来源(`file=... env=...`),便于排查。
|
||||
|
||||
## 发送短信
|
||||
|
||||
### 阿里云
|
||||
接口:`POST /api/v1/sms/send`
|
||||
|
||||
阿里云请求示例:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
@@ -83,13 +101,13 @@ curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
"templateId": "SMS_000000001"
|
||||
},
|
||||
"params": {
|
||||
"phoneNumber": "138****8000",
|
||||
"phoneNumber": "13800008000",
|
||||
"msgContent": "{\"code\":\"123456\"}"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### 腾讯云
|
||||
腾讯云请求示例:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
@@ -104,15 +122,13 @@ curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
"templateId": "000000"
|
||||
},
|
||||
"params": {
|
||||
"phoneNumber": "138****8000",
|
||||
"phoneNumber": "+8613800138000",
|
||||
"msgContent": "123456|5"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
- `serviceType=1`(阿里云)时 `msgContent` 为模板参数 JSON
|
||||
- `serviceType=2`(腾讯云)时多个模板参数用 `|` 分隔
|
||||
- 多个手机号用英文逗号分隔;腾讯云号码使用 E.164 格式
|
||||
`serviceType` 为 `1` 时使用阿里云,`msgContent` 应为模板参数 JSON。`serviceType` 为 `2` 时使用腾讯云,多个模板参数继续沿用原项目的 `|` 分隔格式。多个手机号使用英文逗号分隔,腾讯云号码应使用 E.164 格式。
|
||||
|
||||
成功响应:
|
||||
|
||||
@@ -129,78 +145,62 @@ curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
|
||||
参数错误返回 HTTP 400,上游短信平台调用失败返回 HTTP 502。响应体中的 `code` 同步表示业务结果。
|
||||
|
||||
## 测试实例
|
||||
|
||||
运行不访问真实短信平台的 HTTP 接口测试:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
运行示例客户端(先启动服务并替换示例中的测试凭证):
|
||||
|
||||
```bash
|
||||
go run ./examples/http_client
|
||||
```
|
||||
|
||||
也可以传入其他服务地址:
|
||||
|
||||
```bash
|
||||
go run ./examples/http_client http://127.0.0.1:9090/api/v1/sms/send
|
||||
```
|
||||
|
||||
## gRPC 接口
|
||||
|
||||
接口名:`PbNotice.Notice/Send`。
|
||||
迁移后的接口保持原服务 protobuf 兼容:
|
||||
|
||||
请求中的 `config` 和 `params` 是 JSON 编码的 bytes,字段内容与 HTTP 接口一致;响应结果放在 `head.code` 和 `head.message`,`routerId`、`traceId` 原样返回。
|
||||
```text
|
||||
PbNotice.Notice/Send
|
||||
```
|
||||
|
||||
protobuf 源文件位于 `proto/`,生成的客户端与服务端代码位于 `api/pb/`。
|
||||
请求中的 `config` 和 `params` 是 JSON 编码的 bytes,字段内容与 HTTP 接口一致。
|
||||
响应结果放在 `head.code` 和 `head.message` 中;`routerId`、`traceId` 会原样返回。
|
||||
protobuf 源文件位于 `proto`,生成的客户端和服务端代码位于 `api/pb`。
|
||||
|
||||
使用 Go 示例客户端发送请求:
|
||||
|
||||
```bash
|
||||
go run ./examples/grpc_client examples/requests/aliyun.local.json
|
||||
go run ./examples/grpc_client examples/requests/tencent.local.json 127.0.0.1:11200
|
||||
```
|
||||
|
||||
服务注册了 gRPC reflection,可查看线上接口:
|
||||
|
||||
```bash
|
||||
grpcurl -plaintext 127.0.0.1:11200 list
|
||||
grpcurl -plaintext 127.0.0.1:11200 describe PbNotice.Notice
|
||||
```
|
||||
|
||||
## 容器化部署
|
||||
已保存的 HTTP 请求示例位于 `examples/requests`。其中 `aliyun.json` 和
|
||||
`tencent.json` 使用环境变量占位符,可用于文档和版本管理;同目录的
|
||||
`*.local.json` 是包含本机测试凭证的可直接重放请求,已被 `.gitignore` 排除。
|
||||
|
||||
```bash
|
||||
docker build -t iotlcm-sms:latest -f build/Dockerfile .
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-binary @examples/requests/aliyun.local.json
|
||||
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-binary @examples/requests/tencent.local.json
|
||||
```
|
||||
|
||||
生产部署推荐用不可变版本标签:
|
||||
|
||||
```bash
|
||||
IOTLCM_SMS_IMAGE=<your-registry>/iotlcm-sms:<commit> docker compose -f deploy/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
`deploy/docker-compose.yml` 的约定:
|
||||
|
||||
- HTTP 端口 `11190`,gRPC 端口 `11200`
|
||||
- 真实凭证放在 `deploy/config.yaml`(**不入库**),以只读方式挂载到 `/app/config/config.yaml`
|
||||
- 容器 `read_only: true`,仅 `/tmp` 为 tmpfs,启用 `no-new-privileges`
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
iotlcm-sms/
|
||||
├── main.go # HTTP + gRPC 装配、环境变量注入
|
||||
├── api/pb/ # 生成的 protobuf 代码
|
||||
├── proto/ # protobuf 源文件
|
||||
├── internal/sms/ # Provider 接口 + Registry + 供应商实现
|
||||
│ ├── aliyun/ # 阿里云实现
|
||||
│ └── tencent/ # 腾讯云实现
|
||||
├── internal/httpapi/ # HTTP 路由与处理器
|
||||
├── internal/grpcapi/ # gRPC 服务实现
|
||||
├── examples/ # HTTP / gRPC 示例客户端与请求样例
|
||||
├── tools/ # 辅助工具(如列出阿里云模板)
|
||||
├── build/Dockerfile # 多阶段构建
|
||||
└── deploy/ # docker-compose 编排
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
`go test ./...` 不访问真实短信平台。
|
||||
|
||||
示例客户端:
|
||||
|
||||
```bash
|
||||
go run ./examples/http_client
|
||||
go run ./examples/http_client http://127.0.0.1:9090/api/v1/sms/send
|
||||
go run ./examples/grpc_client examples/requests/aliyun.local.json
|
||||
go run ./examples/grpc_client examples/requests/tencent.local.json 127.0.0.1:11200
|
||||
```
|
||||
|
||||
`examples/requests/aliyun.json`、`tencent.json` 使用环境变量占位符,可入库;同目录的 `*.local.json` 是含本机测试凭证的可直接重放请求,已被 `.gitignore` 排除。
|
||||
|
||||
## 相关项目
|
||||
|
||||
本服务是 [iotlcm](https://git.5iot.com/iotlcm-public) 物联网卡全生命周期管理平台的一部分,负责向客户与设备侧下发短信通知(到期提醒、告警通知等)。
|
||||
|
||||
## License
|
||||
|
||||
[Apache License 2.0](./LICENSE)
|
||||
|
||||
Reference in New Issue
Block a user