init: IotLCM 开源版部署仓

内容:
- docker-compose.yml:14 个基础/业务服务 + 3 个可选 profile(with-mock 挡板、with-bot、with-nginx)
- .env.example:集中管理镜像仓库、口令、端口;必填项缺失时 compose 直接报错退出
- config.example/:10 份配置模板,全部为占位值与注释说明
- schema/:12 个初始化脚本,按文件名顺序由 MySQL initdb 自动执行

脱敏处理:
- 生产/内网 IP、镜像仓库地址、公司名、真实域名全部清除
- 演示账号保留 18 个(出库与划拨依赖账号与部门树的关联),
  昵称/邮箱/手机号/登录IP/头像改为占位或编造值
- 数据库账号与 admin 口令改为初始化时按 .env 注入计算
- 通道模板与 Mock 通道的上游密钥置空
- Mock 通道地址指向 compose 内 onelink-mock 服务

部署时用 --profile with-mock 启动运营商接口挡板,即可在无真实运营商环境时跑通全链路。
This commit is contained in:
MiFaZhan
2026-10-28 13:40:12 +08:00
commit bc2c7bcc6b
26 changed files with 7264 additions and 0 deletions
@@ -0,0 +1,228 @@
# ============================================================
# iotlcm-admin-svr 配置示例
#
# cp config.example.yaml config.yaml
#
# 标注「必改」的项需要按实际环境填写;
# 其余为可运行默认值,按需调整。
# 支持 ${VAR} 与 ${VAR:default} 环境变量占位(由 compose 注入)。
# ============================================================
system:
name: "IotLCM"
version: "1.0"
description: "IotLCM Server"
gfmode: "prod" # prod / dev
upload:
local:
path: "resource/upload"
workerDiagnostic:
enabled: true
server:
address: ":8008"
openapiPath: "/api.json"
swaggerPath: "/swagger"
indexFiles: ["index.html", "index.htm"]
indexFolder: false
fileServerEnabled: true
initLoadMemoryDB: false
maxHeaderBytes: "20KB"
clientMaxBodySize: "200MB"
logger:
path: "/app/logs"
file: "{Y-m-d-H}.log"
prefix: ""
level: "info"
timeFormat: "2006-01-02T15:04:05"
ctxKeys: []
header: true
stdout: true
rotateSize: 0
rotateExpire: 0
rotateBackupLimit: 0
rotateBackupExpire: 0
rotateBackupCompress: 0
rotateCheckInterval: "1h"
stdoutColorDisabled: false
writerColorEnable: false
# ---------- JWT ----------
# 必改:signingKey 必须与 openapi、app-server 完全一致。
jwt:
signingKey: "${JWT_SIGNING_KEY}"
expiresTime: 7d
bufferTime: 1d
issuer: iotlcm
captcha:
keyLong: 6
imgWidth: 240
imgHeight: 80
openCaptcha: 0
openCaptchaTimeout: 3600
# ---------- 数据库 ----------
database:
# 旧平台库(历史数据对接)。不使用时空 link 即可。
legacyCard:
link: ""
debug: false
maxOpen: 5
maxIdle: 2
logger:
path: "/app/logs"
level: "all"
stdout: true
default:
# 必改:连接串格式为 mysql:用户名:口令 再接 tcp(主机:端口)/库名?参数
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
memorydb:
link: "sqlite::@file(:memory:)"
debug: false
maxIdle: 1
maxOpen: 1
maxLifetime: 30
cache:
adapter: "redis"
fileDir: "/app/cache"
batch:
allocate:
chunkSize: 5000
maxFileSize: 52428800 # 50MB
# ---------- 流量预警 ----------
warning:
expire:
firstWarnDays: 30
secondWarnDays: 15
thirdWarnDays: 7
# 短信通道:指向 iotlcm-sms 服务。不用短信时 enabled 改 false。
sms:
enabled: true
url: "http://iotlcm-sms:11190/api/v1/sms/send"
grpcAddr: "iotlcm-sms:11200"
timeout: "5s"
# 以下为短信服务商参数,一般留空(由 sms 服务统一持有)
routerId: 0
serviceType: ""
regionId: ""
accessKeyId: ""
accessKeySecret: ""
sdkAppId: ""
signName: ""
templateId: ""
msgContentTemplate: ""
# 微信小程序订阅消息。不用时 enabled 改 false。
wechat:
enabled: false
appId: ""
appSecret: ""
templateId: ""
page: "pages/message/index"
timeout: "5s"
# ---------- 注册验证码 ----------
register:
# 走 HTTP 短信网关
sms:
url: ""
method: "POST"
headers: {}
bodyTemplate: '{"phone":"{contact}","code":"{code}"}'
successStatus: 200
# 走 SMTP 邮件
email:
host: "" # 如 smtp.example.com
port: 465
username: ""
password: ""
from: ""
fromName: "物联网卡管理平台"
subject: "注册验证码"
bodyTemplate: "您的注册验证码是:{code},6小时内有效。"
ssl: true
# ---------- Redis ----------
redis:
default:
mode: "single" # single / cluster / sentinel
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "SystemCache"
# ---------- RabbitMQ ----------
rabbitmq:
enabled: true
dsn: "amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/"
# ---------- TDengine(流量历史) ----------
# 需要 TDengine 3.x 的 WebSocket 接口(6041)。
# 不启用时序库时 link 留空,历史流量相关功能不可用。
tsdb:
database: "TdEngine"
tdengine:
type: "taosWS"
dsn: "${TDENGINE_USER}:${TDENGINE_PASSWORD}@ws(${TDENGINE_HOST}:${TDENGINE_PORT})/"
dbName: "${TDENGINE_DAILY_DB}"
dailyDbName: "${TDENGINE_DAILY_DB}"
monthlyDbName: "${TDENGINE_MONTHLY_DB}"
maxOpenConns: 100
maxIdleConns: 10
# ---------- 免登录白名单 ----------
router:
exceptLogin:
- /api/v1/system/auth/login
- /api/v1/system/auth/refresh
- /api/v1/system/captcha
- /api/v1/system/auth/getTenantList
- /openapi/v3/query/token
- /api/v1/system/tTplatformSetting/view
- /api/v1/pay/alipay/notify
- /api/v1/pay/alipay/return
# ---------- 支付宝(可选) ----------
# 不使用支付功能时整段删掉。
alipay:
appId: ""
privateKey: ""
alipayPublicKey: ""
notifyUrl: "https://your-domain.example.com/api/v1/pay/alipay/notify"
returnUrl: "https://your-domain.example.com/#/enterpriseManagement/tBalanceDetail/tAgentBalance"
isProduction: false
pay:
expireTime: 60
balanceRecharge:
timeoutDelay: "30m"
# ---------- 上游服务地址 ----------
# 指向同 compose 内的服务名,一般无需修改。
upstream:
# 控制台智能诊断内部凭据,须与 upstream-proxy 的 appDiagnosis.internalToken 一致
appDiagnosisToken: "${DIAGNOSIS_INTERNAL_TOKEN}"
proxy:
address: "upstream-proxy:8510"
timeout: 10s
gateway:
address: "upstream-chan:8110"
timeout: 10s
+61
View File
@@ -0,0 +1,61 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
charset utf-8;
client_max_body_size 200m;
access_log /var/log/nginx/access_admin-ui.log;
error_log /var/log/nginx/error_admin-ui.log;
# 入口 HTML 不强缓存,保证发布后立即生效
location = /index.html {
try_files $uri =404;
add_header Cache-Control no-cache,no-store,must-revalidate always;
add_header Pragma no-cache always;
add_header Expires 0 always;
}
# 运行时配置文件不强缓存
location ~* ^/(?:_app[.]config[.]js|config[.]json|menu-setting[.]json|setting[.]json)$ {
try_files $uri =404;
add_header Cache-Control no-cache,no-store,must-revalidate always;
add_header Pragma no-cache always;
add_header Expires 0 always;
}
# 带 hash 的静态资源长期缓存
location ~* ^/(?:js|jse|css)/ {
try_files $uri =404;
add_header Cache-Control public,max-age=31536000,immutable always;
}
location / {
try_files $uri $uri/ /index.html;
}
# 后端 API 转发(同 compose 内服务名)
location /api/ {
proxy_set_header Connection upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_pass http://admin-svr:8008/api/v1/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 上传文件目录(与 admin-svr 共享同一挂载卷)
location /upload/ {
alias /app/upload/;
}
location /app/upload/ {
alias /app/upload/;
}
gzip on;
gzip_types text/plain application/javascript text/css application/json;
}
@@ -0,0 +1,263 @@
# ============================================================
# iotlcm-app-server 配置示例
#
# cp config.example.yaml config.yaml
#
# 小程序 / H5 端的后端服务。
# ============================================================
system:
name: "IotLCM"
version: "1.0"
description: "IotLCM App Server"
gfmode: "prod"
upload:
local:
path: "resource/upload"
server:
address: ":8010"
openapiPath: "/api.json"
swaggerPath: "/swagger"
indexFiles: ["index.html", "index.htm"]
indexFolder: false
serverRoot: "resource/public"
searchPaths: ["/resource/public"]
fileServerEnabled: true
initLoadMemoryDB: false
maxHeaderBytes: "20KB"
clientMaxBodySize: "200MB"
readTimeout: "20m"
writeTimeout: "20m"
allowedDomains: []
enabledDBOperationLog: false
logger:
path: "/app/resource/log/server"
file: "{Y-m-d-H}.log"
prefix: ""
level: "info"
timeFormat: "2006-01-02T15:04:05"
ctxKeys: []
header: true
stdout: true
rotateSize: 0
rotateExpire: 0
rotateBackupLimit: 0
rotateBackupExpire: 0
rotateBackupCompress: 0
rotateCheckInterval: "1h"
stdoutColorDisabled: false
writerColorEnable: false
# ---------- JWT ----------
# 必改:必须与 admin-svr、openapi 完全一致。
jwt:
signingKey: "${JWT_SIGNING_KEY}"
expiresTime: "7d"
bufferTime: "1d"
issuer: "iotlcm"
captcha:
keyLong: 6
imgWidth: 240
imgHeight: 80
openCaptcha: 0
openCaptchaTimeout: 3600
# ---------- 注册验证码:走短信服务 ----------
register:
sms:
url: "http://iotlcm-sms:11190/api/v1/sms/send"
method: "POST"
headers: {}
bodyTemplate: '{"config":{"scene":"{scene}"},"params":{"phoneNumber":"{contact}","msgContent":"{code}"}}'
successStatus: 200
# ---------- 数据库 ----------
database:
# 旧平台库(历史数据对接)。不使用时空 link 即可。
legacyCard:
link: ""
debug: false
maxOpen: 5
maxIdle: 2
logger:
path: "/app/resource/log/server/sql"
level: "all"
stdout: true
default:
# 必改:连接串
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
memorydb:
link: "sqlite::@file(:memory:)"
debug: false
maxIdle: 1
maxOpen: 1
maxLifetime: 30
cache:
adapter: "redis"
fileDir: "/app/resource/cache"
# ---------- Redis ----------
redis:
default:
mode: "single"
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "AppServerCache"
# ---------- RabbitMQ ----------
rabbitmq:
enabled: true
dsn: "amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/"
# ---------- 异步业务 ----------
workerBatchAsync:
enabled: true
exchange: "iotlcm.batch"
queueName: "iotlcm.batch.execute"
failOnPublish: false
appBusinessAsync:
enabled: true
exchange: "iotlcm.app"
queueName: "iotlcm.app.business"
failOnPublish: false
# ---------- 支付(可选) ----------
# 不使用支付功能时把 enabled 全部改 false。
payment:
balanceRecharge:
enabled: false
mock:
enabled: false
wechat:
enabled: false
envFile: ""
apiBase: "https://api.mch.weixin.qq.com"
mchId: ""
mchSerialNo: ""
apiV3Key: ""
# 微信支付证书需单独放入 data/certs 目录,不随仓库分发
privateKeyPath: "resource/certs/wechatpay_apiclient_key.pem"
notifyUrl: "https://your-domain.example.com/app/api/v1/app/payment/wechat/notify"
notifyBaseUrl: "https://your-domain.example.com/app"
refundNotifyUrl: "https://your-domain.example.com/app/api/v1/app/payment/wechat/refund-notify"
timeoutMinutes: 5
platformPublicKeyId: ""
platformPublicKeyPath: "resource/certs/wechatpay_platform_public_key.pem"
h5:
enabled: false
appId: ""
appName: ""
appUrl: "https://your-domain.example.com/app"
payerClientIp: ""
mini:
enabled: false
appId: ""
appSecret: ""
# ---------- 卡号识别(拍照查卡) ----------
# 走 OpenAI 兼容接口的视觉模型。不需要该功能时 enabled 改 false。
appCardRecognition:
enabled: false
provider: "openai-compatible"
baseUrl: ""
endpoint: ""
model: ""
apiKey: ""
timeout: "25s"
maxImageBytes: 8388608
maxTokens: 256
rateLimit:
enabled: true
count: 20
period: "1m"
# 微信订阅消息模板
appWarning:
wechatSubscribeTemplateId: ""
# ---------- TDengine(流量历史) ----------
tsdb:
database: "TdEngine"
tdengine:
type: "taosWS"
dsn: "${TDENGINE_USER}:${TDENGINE_PASSWORD}@ws(${TDENGINE_HOST}:${TDENGINE_PORT})/"
dbName: "${TDENGINE_DAILY_DB}"
dailyDbName: "${TDENGINE_DAILY_DB}"
monthlyDbName: "${TDENGINE_MONTHLY_DB}"
maxOpenConns: 100
maxIdleConns: 10
# ---------- 免登录白名单 ----------
router:
exceptLogin:
- /api/v1/auth/captcha
- /api/v1/auth/side-captcha
- /api/v1/auth/check-side-captcha
- /api/v1/auth/sms
- /api/v1/auth/register
- /api/v1/auth/login
- /api/v1/auth/wechat-login
- /api/v1/auth/wechat-bind-phone
- /api/v1/auth/forgot-password
- /api/v1/card/public-query
- /api/v1/card/recognize
- /api/v1/card/public-package-detail
- /api/v1/open/diagnosis/info
- /api/v1/open/diagnosis/flow
- /api/v1/payment/wechat/notify
- /api/v1/payment/wechat/refund-notify
- /api/v1/app/auth/captcha
- /api/v1/app/auth/side-captcha
- /api/v1/app/auth/check-side-captcha
- /api/v1/app/auth/sms
- /api/v1/app/auth/register
- /api/v1/app/auth/login
- /api/v1/app/auth/wechat-login
- /api/v1/app/auth/wechat-bind-phone
- /api/v1/app/auth/forgot-password
- /api/v1/app/card/public-query
- /api/v1/app/card/recognize
- /api/v1/app/card/public-package-detail
- /api/v1/app/open/diagnosis/info
- /api/v1/app/open/diagnosis/flow
- /api/v1/app/payment/wechat/notify
- /api/v1/app/payment/wechat/refund-notify
- /openapi/v3/query/token
# ---------- 上游服务地址 ----------
upstream:
# 控制台智能诊断内部凭据,须与 admin-svr 一致
appDiagnosisToken: "${DIAGNOSIS_INTERNAL_TOKEN}"
provider: "onelink_mock"
default:
clientName: "http://upstream-proxy:8510"
timeout: "10s"
gateway:
clientName: "http://upstream-chan:8110"
timeout: "10s"
# 移动 OneLink 挡板(联调用)。对接真实运营商时 enabled 改 false。
onelinkMock:
enabled: false
baseUrl: "http://onelink-mock:8080/v5"
appid: ""
password: ""
timeout: "10s"
tokenTTL: 3600
accounts: []
+26
View File
@@ -0,0 +1,26 @@
# ============================================================
# iotlcm-bot 配置示例
#
# cp config.example.yaml config.yaml
#
# 本服务把全量同步等运维事件推送到飞书群。
# 不使用飞书通知时不要启动该容器(默认走 with-bot profile)。
# ============================================================
environment: prod
# 全量同步归档目录与状态目录(容器内路径,compose 已挂载)
archiveDir: /data/full-sync
stateDir: /data/state
# 轮询间隔
pollInterval: 10s
# ---------- 飞书 ----------
# 在飞书开放平台创建自建应用后获取:
# appId / appSecret:应用凭证
# chatId:目标群会话 ID
feishu:
appId: "${FEISHU_APP_ID}"
appSecret: "${FEISHU_APP_SECRET}"
chatId: "${FEISHU_CHAT_ID}"
+141
View File
@@ -0,0 +1,141 @@
# ============================================================
# IotLCM 对外入口 nginx 配置示例
#
# 作用:把多个服务按路径分流到统一域名下。
# 若宿主机已有 nginx 或面板,可不启动本容器,
# 把下面的 location 段落并入宿主机配置即可。
#
# 把 your-domain.example.com 替换为你的实际域名。
# ============================================================
upstream iotlcm_admin_api {
server admin-svr:8008;
}
upstream iotlcm_admin_ui {
server admin-ui:80;
}
upstream iotlcm_openapi_api {
server openapi:8000;
}
upstream iotlcm_docs {
server docs:80;
}
upstream iotlcm_app_web {
server app-web:80;
}
upstream iotlcm_app_server {
server app-server:8010;
}
# ---------- OpenAPI 对外域名 ----------
server {
listen 80;
server_name api.your-domain.example.com;
client_max_body_size 200m;
location ^~ /openapi/v3/ {
proxy_pass http://iotlcm_openapi_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# 运营商能力代理(白名单转发 / 共享 Token)
location ^~ /route/open/ {
proxy_pass http://iotlcm_openapi_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
location / {
proxy_pass http://iotlcm_openapi_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# ---------- 管理端 ----------
server {
listen 80;
server_name admin.your-domain.example.com;
client_max_body_size 200m;
location / {
proxy_pass http://iotlcm_admin_ui;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 文档站(子路径 /docs/)
location = /docs {
return 301 /docs/;
}
location /docs/ {
proxy_pass http://iotlcm_docs;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# ---------- 小程序 / H5 ----------
server {
listen 80;
server_name app.your-domain.example.com;
client_max_body_size 200m;
location ^~ /app/api/v1/ {
proxy_pass http://iotlcm_app_server/api/v1/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
location ^~ /app/ {
proxy_pass http://iotlcm_app_web;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://iotlcm_app_web;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
@@ -0,0 +1,91 @@
# ============================================================
# iotlcm-openapi 配置示例
#
# cp config.example.yaml config.yaml
# ============================================================
system:
name: "IotLCM-OpenAPI"
version: "1.0"
description: "IotLCM OpenAPI Service"
gfmode: "prod"
server:
address: ":8000"
openapiPath: "/api.json"
swaggerPath: "/swagger"
maxHeaderBytes: "20KB"
clientMaxBodySize: "200MB"
readTimeout: "20m"
writeTimeout: "20m"
logger:
path: "/app/logs"
file: "{Y-m-d-H}.log"
level: "info"
timeFormat: "2006-01-02T15:04:05"
stdout: true
header: true
# ---------- JWT ----------
# 必改:必须与 admin-svr、app-server 完全一致,
# 否则 admin 签发的令牌在 openapi 侧校验不通过。
jwt:
signingKey: "${JWT_SIGNING_KEY}"
expiresTime: "7d"
bufferTime: "1d"
issuer: "iotlcm"
# ---------- 数据库 ----------
# 建议为 OpenAPI 单独建只读账号,与 admin 的账号隔离。
database:
logger:
path: "/app/logs/sql"
level: "all"
stdout: true
default:
# 必改:连接串
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
cache:
adapter: "redis"
fileDir: "resource/cache"
# ---------- Redis ----------
redis:
default:
mode: "single"
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "OpenAPICache"
# ---------- RabbitMQ ----------
rabbitmq:
enabled: true
dsn: "amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/"
# ---------- 免登录白名单 ----------
router:
exceptLogin:
- /openapi/v3/query/token
# ---------- 上游服务地址 ----------
upstream:
proxy:
address: "upstream-proxy:8510"
timeout: "10s"
gateway:
address: "upstream-chan:8110"
timeout: "10s"
+29
View File
@@ -0,0 +1,29 @@
# ============================================================
# iotlcm-sms 配置示例
#
# cp config.example.yaml config.yaml
#
# 短信服务商的全局凭证与签名由本服务持有,
# 调用方(admin-svr / app-server)只传模板 ID 与短信内容。
# ============================================================
# 服务商:1=阿里云,2=腾讯云
serviceType: "1"
# ---------- 必改:服务商凭据 ----------
regionId: cn-hangzhou
accessKeyId: "${SMS_ACCESS_KEY_ID}"
accessKeySecret: "${SMS_ACCESS_KEY_SECRET}"
# 短信签名,需已在服务商侧审核通过
signName: "示例签名"
# 仅腾讯云需要:
# sdkAppId: ""
# ---------- 场景模板库(可选) ----------
# 配置后按 scene 从 t_sms_template_config 表读取模板与签名,
# 改库即生效(短 TTL 缓存)。
# 不配置则 scene 功能关闭,调用方必须自带 templateId。
sceneDbDsn: "u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=true&loc=Local"
sceneDbCacheTtl: 60s
@@ -0,0 +1,108 @@
# ============================================================
# iotlcm-upstream-chan 配置示例
#
# cp config.example.yaml config.yaml
#
# 本服务负责具体运营商协议对接(移动 OneLink / 电信 CMP /
# 联通 10646 / 各类三方渠道)。
#
# 注意:接入运营商需要在数据库中配置通道(t_sim_channels 等),
# 本文件只配置服务自身参数。
# ============================================================
system:
name: "IotLCM-Upstream-Chan"
version: "1.0"
description: "IotLCM Upstream Channel Service"
gfmode: "prod"
upload:
local:
path: "/app/upload"
server:
name: "iotlcmupstreamchan-http-svc"
address: ":8110"
openapiPath: "/api.json"
swaggerPath: "/swagger"
indexFiles: ["index.html", "index.htm"]
indexFolder: false
fileServerEnabled: true
initLoadMemoryDB: false
maxHeaderBytes: "20KB"
clientMaxBodySize: "200MB"
# ---------- 服务注册 ----------
registry:
type: "consul"
address: "consul:8500"
logger:
path: "/app/logs"
file: "{Y-m-d-H}.log"
prefix: ""
level: "info"
timeFormat: "2006-01-02T15:04:05.000"
ctxKeys: []
header: true
stdout: true
rotateSize: 0
rotateExpire: 0
rotateBackupLimit: 0
rotateBackupExpire: 0
rotateBackupCompress: 0
rotateCheckInterval: "1h"
stdoutColorDisabled: false
writerColorEnable: false
# ---------- 数据库 ----------
database:
logger:
path: "/app/logs"
level: "all"
stdout: true
default:
# 必改:连接串
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
memorydb:
link: "sqlite::@file(:memory:)"
debug: false
maxIdle: 1
maxOpen: 1
maxLifetime: 30
cache:
adapter: "redis"
fileDir: "resource/cache"
# ---------- Redis ----------
redis:
default:
mode: "single"
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "SystemCache"
# ---------- 依赖行为 ----------
dependencies:
# 运营商 Token 刷新策略
token_refresh:
mode: "expired_only" # expired_only / always
skew_seconds: 600 # 提前刷新的时间窗(秒)
# 移动 OneLink 挡板地址。
# 启动了 onelink-mock(--profile with-mock)时保持此值;
# 对接真实运营商时删掉本段或改为真实地址。
sim_yd_mock_v5:
url: "http://onelink-mock:8080/v5/"
@@ -0,0 +1,110 @@
# ============================================================
# iotlcm-upstream-proxy 配置示例
#
# cp config.example.yaml config.yaml
#
# 本服务是通道侧的缓存与转发层,通常不需要改业务参数。
# ============================================================
system:
name: "IotLCM-Upstream-Proxy"
version: "1.0"
description: "IotLCM Upstream Proxy Service"
gfmode: "prod"
upload:
local:
path: "/app/upload"
# ---------- 智能诊断内部凭据 ----------
# 控制台「智能诊断」调用本服务的内部凭据,
# 必须与 admin-svr 的 upstream.appDiagnosisToken 一致。
# 生成:openssl rand -hex 16
appDiagnosis:
internalToken: "${DIAGNOSIS_INTERNAL_TOKEN}"
server:
name: "iotlcmupstreamproxy-grpc-svc"
address: ":8510"
openapiPath: "/api.json"
swaggerPath: "/swagger"
indexFiles: ["index.html", "index.htm"]
indexFolder: false
fileServerEnabled: true
initLoadMemoryDB: false
maxHeaderBytes: "20KB"
clientMaxBodySize: "200MB"
# ---------- 服务注册 ----------
registry:
type: "consul"
address: "consul:8500"
logger:
path: "/app/logs"
file: "{Y-m-d-H}.log"
prefix: ""
level: "info"
timeFormat: "2006-01-02T15:04:05.000"
ctxKeys: []
header: true
stdout: true
rotateSize: 0
rotateExpire: 0
rotateBackupLimit: 0
rotateBackupExpire: 0
rotateBackupCompress: 0
rotateCheckInterval: "1h"
stdoutColorDisabled: false
writerColorEnable: false
# ---------- 数据库 ----------
database:
logger:
path: "/app/logs"
level: "all"
stdout: true
default:
# 必改:连接串
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
memorydb:
link: "sqlite::@file(:memory:)"
debug: false
maxIdle: 1
maxOpen: 1
maxLifetime: 30
cache:
adapter: "redis"
fileDir: "resource/cache"
# ---------- Redis ----------
redis:
default:
mode: "single"
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "SystemCache"
# ---------- 上游通道服务 ----------
gateway:
# 通道服务地址(同 compose 内服务名)
address: "upstream-chan"
timeout: 10s
# 单次批量转发的总预算上限。
# 非 APN 批量按「单卡超时 × 卡数」计算并以此封顶,
# 避免通道逐卡串行办理时请求仍在执行就被判失败
# (批量重启、停复机、批量查询)。
# worker 侧的超时配置必须大于该值。
batchTimeout: 20m
@@ -0,0 +1,139 @@
# ============================================================
# iotlcm-worker-svr 配置示例
#
# cp config.example.yaml config.yaml
#
# 本服务是异步中枢:消费 RabbitMQ 队列,执行批次开停复机、
# 全量同步、流量统计等后台任务。
# ============================================================
system:
name: "IotLCM"
version: "1.0"
description: "IotLCM Worker"
gfmode: "prod"
upload:
local:
path: "resource/upload"
logger:
path: "/app/logs"
file: "{Y-m-d-H}.log"
prefix: ""
level: "all"
timeFormat: "2006-01-02T15:04:05"
ctxKeys: []
header: true
stdout: true
rotateSize: 0
rotateExpire: 0
rotateBackupLimit: 0
rotateBackupExpire: 0
rotateBackupCompress: 0
rotateCheckInterval: "1h"
stdoutColorDisabled: false
writerColorEnable: false
# ---------- 数据库 ----------
database:
logger:
path: "/app/logs/sql"
level: "all"
stdout: true
default:
# 必改:连接串
link: "mysql:u_iotlcm:${MYSQL_APP_PASSWORD}@tcp(mysql:3306)/${MYSQL_DATABASE}?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
debug: false
charset: "utf8mb4"
dryRun: false
maxIdle: 30
maxOpen: 100
maxLifetime: 120
memorydb:
link: "sqlite::@file(:memory:)"
debug: false
maxIdle: 1
maxOpen: 1
maxLifetime: 30
cache:
adapter: "redis"
fileDir: "resource/cache"
# ---------- Redis ----------
redis:
default:
mode: "single"
sentinelMasterName: ""
address: "redis:6379"
db: 0
pass: "${REDIS_PASSWORD}"
minIdle: 5
maxIdle: 30
maxActive: 5000
idleTimeout: "60s"
ClientName: "WorkerCache"
# ---------- RabbitMQ ----------
rabbitmq:
enabled: true
dsn: "amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/"
balanceRecharge:
retryDelay: "1m"
batchExecute:
executionTimeout: "0s"
finalizationTimeout: "60s"
retryDelay: "30s"
maxRetries: 3
# ---------- 批次执行 ----------
batch:
allocate:
processingRetryAfter: "30m"
inbound:
upstreamBatchSize: 1
upstreamConcurrency: 300
# 运营商接口限流。必须按上游实际配额设置,
# 调太大会被运营商限流甚至封禁。
upstreamRateLimit:
enabled: true
mode: "redis"
redisGroup: "default"
keyPrefix: "iotlcm:worker:inbound:upstream_basic_info"
redisFailureFallback: "error"
default:
enabled: false
carriers:
mobile:
enabled: false
telecom:
enabled: true
qps: 20
burst: 1
requestCost: 1
channels: {}
stockQueryBatchSize: 1000
writeBatchSize: 1000
# ---------- TDengine(流量历史) ----------
# 不启用时序库时 dsn 留空,全量同步中的流量历史部分不可用。
tsdb:
database: "TdEngine"
tdengine:
type: "taosWS"
dsn: "${TDENGINE_USER}:${TDENGINE_PASSWORD}@ws(${TDENGINE_HOST}:${TDENGINE_PORT})/"
# dbName 只是连接池键名与兜底值,不决定实际访问哪个库
dbName: "${TDENGINE_DAILY_DB}"
dailyDbName: "${TDENGINE_DAILY_DB}"
monthlyDbName: "${TDENGINE_MONTHLY_DB}"
maxOpenConns: 100
maxIdleConns: 10
# ---------- 上游服务地址 ----------
upstream:
proxy:
address: "upstream-proxy:8510"
# 异步提交协议版本,保持默认
apnAsyncProtocolVersion: 1
timeout: 10s