# 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 /iotlcm-sms:latest . docker push /iotlcm-sms:latest cd deploy # 先在服务器放置真实 config.yaml(不入库) docker compose pull docker compose up -d ``` 生产部署建议通过 `IOTLCM_SMS_IMAGE` 指定不可变版本标签: ```bash IOTLCM_SMS_IMAGE=/iotlcm-sms: 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 ```