MiFaZhan 6c26f7a0e1 fix: 删除从 builder 拷贝 zoneinfo 的指令
原 Dockerfile 依赖私有基础镜像预装的 tzdata;换成公共 golang:alpine 后
builder 阶段不存在 /usr/share/zoneinfo/Asia/Shanghai,构建报
'failed to compute cache key: not found'。

runtime 阶段已 apk add tzdata + ENV TZ=Asia/Shanghai,该 COPY 多余,直接删除。
2026-10-29 13:42:17 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00
2026-10-22 14:21:47 +08:00

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 查看线上接口

快速开始

go mod tidy
go run .

默认监听 :8080,gRPC 默认监听 :11200:

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。

# 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=...),便于排查。

发送短信

阿里云

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\"}"
    }
  }'

腾讯云

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 格式

成功响应:

{
  "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/。

grpcurl -plaintext 127.0.0.1:11200 list
grpcurl -plaintext 127.0.0.1:11200 describe PbNotice.Notice

容器化部署

docker build -t iotlcm-sms:latest -f build/Dockerfile .

生产部署推荐用不可变版本标签:

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

目录结构

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 编排

测试

go test ./...

go test ./... 不访问真实短信平台。

示例客户端:

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 物联网卡全生命周期管理平台的一部分,负责向客户与设备侧下发短信通知(到期提醒、告警通知等)。

License

Apache License 2.0

S
Description
iotLCM 物联网卡全生命周期管理平台(开源版,单仓多服务)
Readme Apache-2.0 38 MiB
Languages
Go 69.4%
Vue 15.2%
TypeScript 12%
PLpgSQL 2.4%
JavaScript 0.4%
Other 0.3%