# iotlcm-sms 独立短信发送服务,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`: ```bash HTTP_ADDR=:9090 GRPC_ADDR=:11201 go run . ``` 健康检查: ```bash 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=...`),便于排查。 ## 发送短信 ### 阿里云 ```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": "138****8000", "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": "138****8000", "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` 同步表示业务结果。 ## gRPC 接口 接口名:`PbNotice.Notice/Send`。 请求中的 `config` 和 `params` 是 JSON 编码的 bytes,字段内容与 HTTP 接口一致;响应结果放在 `head.code` 和 `head.message`,`routerId`、`traceId` 原样返回。 protobuf 源文件位于 `proto/`,生成的客户端与服务端代码位于 `api/pb/`。 ```bash grpcurl -plaintext 127.0.0.1:11200 list grpcurl -plaintext 127.0.0.1:11200 describe PbNotice.Notice ``` ## 容器化部署 ```bash docker build -t iotlcm-sms:latest -f build/Dockerfile . ``` 生产部署推荐用不可变版本标签: ```bash IOTLCM_SMS_IMAGE=/iotlcm-sms: 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)