chore: 并入 iotlcm-sms 历史到 sms/(原提交 1d38bc4)
This commit is contained in:
+206
@@ -0,0 +1,206 @@
|
||||
# iotlcm-sms
|
||||
|
||||
独立短信 HTTP/gRPC 服务,支持阿里云和腾讯云短信。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `:8080`,可通过 `HTTP_ADDR` 修改:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
健康检查:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/healthz
|
||||
```
|
||||
|
||||
## 发送短信
|
||||
|
||||
接口:`POST /api/v1/sms/send`
|
||||
|
||||
阿里云请求示例:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"config": {
|
||||
"serviceType": "1",
|
||||
"regionId": "cn-hangzhou",
|
||||
"accessKeyId": "your-access-key-id",
|
||||
"accessKeySecret": "your-access-key-secret",
|
||||
"signName": "your-sign-name",
|
||||
"templateId": "SMS_000000001"
|
||||
},
|
||||
"params": {
|
||||
"phoneNumber": "13800008000",
|
||||
"msgContent": "{\"code\":\"123456\"}"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
腾讯云请求示例:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"config": {
|
||||
"serviceType": "2",
|
||||
"sdkAppId": "1400000000",
|
||||
"accessKeyId": "your-secret-id",
|
||||
"accessKeySecret": "your-secret-key",
|
||||
"signName": "your-sign-name",
|
||||
"templateId": "000000"
|
||||
},
|
||||
"params": {
|
||||
"phoneNumber": "+8613800138000",
|
||||
"msgContent": "123456|5"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
`serviceType` 为 `1` 时使用阿里云,`msgContent` 应为模板参数 JSON。`serviceType` 为 `2` 时使用腾讯云,多个模板参数继续沿用原项目的 `|` 分隔格式。多个手机号使用英文逗号分隔,腾讯云号码应使用 E.164 格式。
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "发送成功",
|
||||
"data": {
|
||||
"provider": "aliyun",
|
||||
"requestId": "provider-request-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
参数错误返回 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 接口
|
||||
|
||||
迁移后的接口保持原服务 protobuf 兼容:
|
||||
|
||||
```text
|
||||
PbNotice.Notice/Send
|
||||
```
|
||||
|
||||
请求中的 `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
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user