chore: 并入 iotlcm-sms 历史到 sms/(原提交 1d38bc4)

This commit is contained in:
MiFaZhan
2026-11-02 13:28:02 +08:00
34 changed files with 2776 additions and 0 deletions
+206
View File
@@ -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
```