chore: 并入 iotlcm-deploy 历史到 deploy/(原提交 eb97c53)
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
name: mirror-to-gitee
|
||||||
|
|
||||||
|
# 把本仓同步到 Gitee 企业仓 snowgoat/<同名仓库>。
|
||||||
|
# 触发:main 分支推送、v* tag 推送、手动;另有每日兜底重试。
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
schedule:
|
||||||
|
# Gitea Actions 的 cron 用 UTC:18:30 UTC = 次日 02:30 北京时间
|
||||||
|
- cron: '30 18 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: mirror-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mirror:
|
||||||
|
runs-on: host
|
||||||
|
steps:
|
||||||
|
- name: 同步到 Gitee
|
||||||
|
env:
|
||||||
|
SRC_TOKEN: ${{ secrets.SRC_GIT_TOKEN }}
|
||||||
|
DST_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||||||
|
DST_OWNER: ${{ secrets.GITEE_OWNER }}
|
||||||
|
DST_USER: ${{ secrets.GITEE_USER }}
|
||||||
|
REPO_NAME: ${{ github.event.repository.name }}
|
||||||
|
run: |
|
||||||
|
set -u
|
||||||
|
|
||||||
|
if [ -z "${REPO_NAME:-}" ]; then
|
||||||
|
echo "未取到仓库名,退出"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
echo "源:git.5iot.com/iotlcm-public/$REPO_NAME"
|
||||||
|
echo "目标:gitee.com/$DST_OWNER/$REPO_NAME"
|
||||||
|
|
||||||
|
git clone --mirror "https://jenkins:${SRC_TOKEN}@git.5iot.com/iotlcm-public/${REPO_NAME}.git" "$WORK/repo.git"
|
||||||
|
cd "$WORK/repo.git"
|
||||||
|
echo "本地 ref:$(git for-each-ref --format='%(refname:short)' | tr '\n' ' ')"
|
||||||
|
|
||||||
|
# Gitee 在推送密集时会返回 429 Pending queue overflowed / 408 Authorization server timeout,
|
||||||
|
# 属服务端限流,退避重试即可。
|
||||||
|
attempt=0
|
||||||
|
while [ "$attempt" -lt 5 ]; do
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
echo "----- 推送尝试 $attempt/5"
|
||||||
|
if git push --mirror "https://${DST_USER}:${DST_TOKEN}@gitee.com/${DST_OWNER}/${REPO_NAME}.git" 2>&1; then
|
||||||
|
echo "✅ 已同步到 Gitee"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
wait_s=$((attempt * 30))
|
||||||
|
echo "推送失败,${wait_s}s 后重试"
|
||||||
|
sleep "$wait_s"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "❌ 连续 5 次推送失败,请检查 Gitee 状态或令牌"
|
||||||
|
exit 1
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# 运行时数据(含数据库文件、日志、上传件)
|
||||||
|
data/
|
||||||
|
|
||||||
|
# 本地覆盖配置(如需保留自己的修改而不进版本库,可放 config/local.yaml)
|
||||||
|
config/*/local.yaml
|
||||||
|
|
||||||
|
# 编辑器与系统文件
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# IotLCM 部署仓
|
||||||
|
|
||||||
|
IotLCM 物联网卡管理平台的部署编排与数据库初始化脚本。使用 Docker Compose 在单机上拉起完整平台。
|
||||||
|
|
||||||
|
## 一键启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <本仓库地址> iotlcm-deploy
|
||||||
|
cd iotlcm-deploy
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
无需复制或修改任何文件,配置里已带可用默认值。首次启动会自动完成 MySQL 建库建表(95 张)、种子数据导入、时序库建表,实测约 30 秒~1 分钟。
|
||||||
|
|
||||||
|
等待 MySQL 健康后其余服务依次启动,观察进度:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
启动完成后访问 `http://<宿主机IP>:8009`:
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 账号 | `admin` |
|
||||||
|
| 口令 | `Iotlcm2026Default` |
|
||||||
|
|
||||||
|
> ⚠️ **正式使用前必须修改默认口令**,见下方「上线前必改」。
|
||||||
|
|
||||||
|
## 可选服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock up -d # 运营商接口挡板
|
||||||
|
docker compose --profile with-bot up -d # 飞书通知(运维自用)
|
||||||
|
docker compose --profile with-nginx up -d # 自带反向代理
|
||||||
|
```
|
||||||
|
|
||||||
|
多个 profile 可组合:`--profile with-mock --profile with-nginx up -d`
|
||||||
|
|
||||||
|
`with-bot` 是把全量同步等运维事件推送到飞书群的**内部运维工具**,不是平台功能。它需要你自己的飞书自建应用凭证(`config/bot/config.yaml` 的 `appId` / `appSecret` / `chatId`),没填就不启动。
|
||||||
|
|
||||||
|
**默认不启动,也不影响平台功能**;不需要飞书通知就完全不用管它。
|
||||||
|
|
||||||
|
## 镜像版本
|
||||||
|
|
||||||
|
所有镜像(含基础组件)都在**同一个镜像仓库** `<镜像仓库>/iotlcm/iotlcm` 下,靠 tag 区分服务与版本:
|
||||||
|
|
||||||
|
```text
|
||||||
|
crpi-.../iotlcm/iotlcm:mysql-8.0
|
||||||
|
crpi-.../iotlcm/iotlcm:redis-7.2
|
||||||
|
crpi-.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4
|
||||||
|
crpi-.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
tag 格式统一为 **`<服务名>-<版本>`**。
|
||||||
|
|
||||||
|
当前业务版本:大部分服务为 `v1.0.4`,`iotlcm-app-server` / `iotlcm-app-web` 为 `v1.0.2`(源仓最新 tag 就是 v1.0.2)。
|
||||||
|
|
||||||
|
升级时在 `docker-compose.yml` 中同步更新 tag 的版本部分,不要改用 `latest`。
|
||||||
|
|
||||||
|
## 部署内容
|
||||||
|
|
||||||
|
| 类别 | 服务 |
|
||||||
|
| --- | --- |
|
||||||
|
| 基础组件 | MySQL、Redis、RabbitMQ、Consul、TDengine(流量历史) |
|
||||||
|
| 业务服务 | admin-svr(管理端后端)、admin-ui(管理端前端)、openapi(对外 API)、app-server(小程序后端)、app-web(小程序前端)、upstream-chan(运营商对接)、upstream-proxy(通道缓存转发)、worker-svr(异步任务)、sms(短信)、docs(文档站) |
|
||||||
|
| 可选 | onelink-mock(运营商接口挡板)、bot(飞书通知)、nginx(反向代理) |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
iotlcm-deploy/
|
||||||
|
├── docker-compose.yml # 服务编排(含全部默认值)
|
||||||
|
├── config/ # 各服务配置,已带可用默认值
|
||||||
|
│ ├── admin-svr/config.yaml
|
||||||
|
│ ├── admin-ui/default.conf
|
||||||
|
│ ├── app-server/config.yaml
|
||||||
|
│ ├── bot/config.yaml
|
||||||
|
│ ├── nginx/iotlcm.conf
|
||||||
|
│ ├── openapi/config.yaml
|
||||||
|
│ ├── sms/config.yaml
|
||||||
|
│ ├── upstream-chan/config.yaml
|
||||||
|
│ ├── upstream-proxy/config.yaml
|
||||||
|
│ └── worker-svr/config.yaml
|
||||||
|
├── schema/init.sql # MySQL 初始化(单文件,首次启动自动执行)
|
||||||
|
├── tdengine-init/ # TDengine 建库建表(首次启动自动执行)
|
||||||
|
└── data/ # 运行数据(首次启动自动创建,不入版本库)
|
||||||
|
```
|
||||||
|
|
||||||
|
全新初始化生成 95 张业务表(原有 91 张 + worker 异步恢复所需 4 张);已部署旧库不会自动执行新增迁移,见 `schema/README.md`。
|
||||||
|
|
||||||
|
编排已内置 TDengine(流量历史库),由 `tdengine-init` 服务在 TDengine 健康后自动建库建表:
|
||||||
|
|
||||||
|
| 库 | 分片 | 保留 | 超级表 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `iotlcm_daily` | 7 天 | 3 年 | `t_sim_card_usage_daily` |
|
||||||
|
| `iotlcm_monthly` | 30 天 | 3 年 | `t_sim_card_usage_monthly` |
|
||||||
|
|
||||||
|
结构取自生产环境,`tdengine-init` 会校验建表结果,缺表则报错退出。业务代码只读写不建表,因此这一步不能跳过。
|
||||||
|
|
||||||
|
TDengine 版本为 `3.3.6.13`(公开镜像可得),生产用的是 `3.4.0.9`。两者在流量历史的读写上一致;如需与生产完全对齐,可自行构建镜像并替换 `docker-compose.yml` 里的 tag。
|
||||||
|
|
||||||
|
## 上线前必改
|
||||||
|
|
||||||
|
默认口令仅用于快速试用。**正式部署前必须修改以下内容**,具体位置见[部署手册](https://git.5iot.com/iotlcm-public/iotlcm-docs):
|
||||||
|
|
||||||
|
| 项 | 位置 |
|
||||||
|
| --- | --- |
|
||||||
|
| 数据库口令(root 与应用账号) | `docker-compose.yml` + `config/*/config.yaml` + `schema/init.sql` |
|
||||||
|
| Redis 口令 | `docker-compose.yml` + `config/*/config.yaml` |
|
||||||
|
| RabbitMQ 口令 | `docker-compose.yml` + `config/*/config.yaml` |
|
||||||
|
| admin 初始口令 | `docker-compose.yml` + `schema/init.sql` |
|
||||||
|
| JWT 签名密钥 | `config/admin-svr`、`config/openapi`、`config/app-server` 三处 |
|
||||||
|
| 对外域名 | `config/nginx/iotlcm.conf` |
|
||||||
|
|
||||||
|
::: warning 改口令必须多处同步
|
||||||
|
因为配置文件里是字面值(未使用环境变量),改口令时**编排、配置、初始化脚本三处必须一致**,漏改会导致服务连不上数据库。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 重要提示
|
||||||
|
|
||||||
|
- **`JWT_SIGNING_KEY` 必须在 `admin-svr`、`openapi`、`app-server` 三者间完全一致**,否则令牌跨服务不通
|
||||||
|
- **镜像标签不要用 `latest`**,生产环境请固定到具体版本
|
||||||
|
- **改 `schema/` 下的脚本不会自动生效**,需要清空 `data/mysql` 重新初始化(会丢失全部数据)
|
||||||
|
- 默认只监听 `127.0.0.1` 的服务需通过反向代理对外
|
||||||
|
|
||||||
|
## 许可
|
||||||
|
|
||||||
|
[Apache License 2.0](LICENSE)
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-admin-svr 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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: "1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e"
|
||||||
|
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:
|
||||||
|
# 使用与其余服务相同的 u_iotlcm 应用账号。
|
||||||
|
link: "mysql:u_iotlcm:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
minIdle: 5
|
||||||
|
maxIdle: 30
|
||||||
|
maxActive: 5000
|
||||||
|
idleTimeout: "60s"
|
||||||
|
ClientName: "SystemCache"
|
||||||
|
|
||||||
|
# ---------- RabbitMQ ----------
|
||||||
|
rabbitmq:
|
||||||
|
enabled: true
|
||||||
|
dsn: "amqp://iotlcm:Iotlcm2026Default@rabbitmq:5672/"
|
||||||
|
|
||||||
|
# ---------- TDengine(流量历史) ----------
|
||||||
|
# 需要 TDengine 3.x 的 WebSocket 接口(6041)。
|
||||||
|
# 不启用时序库时 link 留空,历史流量相关功能不可用。
|
||||||
|
tsdb:
|
||||||
|
database: "TdEngine"
|
||||||
|
tdengine:
|
||||||
|
type: "taosWS"
|
||||||
|
# TDengine 3.x WebSocket 地址(容器内服务名 tdengine,端口 6041)
|
||||||
|
# 口令为 TDengine 默认 root/taosdata;改过口令需同步改这里
|
||||||
|
dsn: "root:taosdata@ws(tdengine:6041)/"
|
||||||
|
# dbName 只是连接池键名与兜底值,不决定实际访问哪个库
|
||||||
|
dbName: "iotlcm_daily"
|
||||||
|
dailyDbName: "iotlcm_daily"
|
||||||
|
monthlyDbName: "iotlcm_monthly"
|
||||||
|
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: "iotlcm_diagnosis_2026"
|
||||||
|
proxy:
|
||||||
|
address: "upstream-proxy:8510"
|
||||||
|
timeout: 10s
|
||||||
|
gateway:
|
||||||
|
address: "upstream-chan:8110"
|
||||||
|
timeout: 10s
|
||||||
@@ -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,266 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-app-server 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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: "1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e"
|
||||||
|
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:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
minIdle: 5
|
||||||
|
maxIdle: 30
|
||||||
|
maxActive: 5000
|
||||||
|
idleTimeout: "60s"
|
||||||
|
ClientName: "AppServerCache"
|
||||||
|
|
||||||
|
# ---------- RabbitMQ ----------
|
||||||
|
rabbitmq:
|
||||||
|
enabled: true
|
||||||
|
dsn: "amqp://iotlcm:Iotlcm2026Default@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"
|
||||||
|
# TDengine 3.x WebSocket 地址(容器内服务名 tdengine,端口 6041)
|
||||||
|
# 口令为 TDengine 默认 root/taosdata;改过口令需同步改这里
|
||||||
|
dsn: "root:taosdata@ws(tdengine:6041)/"
|
||||||
|
# dbName 只是连接池键名与兜底值,不决定实际访问哪个库
|
||||||
|
dbName: "iotlcm_daily"
|
||||||
|
dailyDbName: "iotlcm_daily"
|
||||||
|
monthlyDbName: "iotlcm_monthly"
|
||||||
|
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: "iotlcm_diagnosis_2026"
|
||||||
|
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: []
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-bot 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 说明:本服务把全量同步等运维事件推送到飞书群,属可选的运维通知工具,
|
||||||
|
# 不是平台功能本身。默认不启动(需显式 --profile with-bot),
|
||||||
|
# 不启动不影响任何平台功能。
|
||||||
|
|
||||||
|
environment: prod
|
||||||
|
|
||||||
|
# 全量同步归档目录与状态目录(容器内路径,compose 已挂载)
|
||||||
|
archiveDir: /data/full-sync
|
||||||
|
stateDir: /data/state
|
||||||
|
|
||||||
|
# 轮询间隔
|
||||||
|
pollInterval: 10s
|
||||||
|
|
||||||
|
# ---------- 飞书(自建应用,自己的凭证) ----------
|
||||||
|
# appId / appSecret:在飞书开放平台创建自建应用后获取
|
||||||
|
# chatId:接收通知的群会话 ID
|
||||||
|
feishu:
|
||||||
|
appId: ""
|
||||||
|
appSecret: ""
|
||||||
|
chatId: ""
|
||||||
@@ -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,93 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-openapi 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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: "1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e1f8b3c5d7e9a2b4c6d8e0f1a3b5c7d9e"
|
||||||
|
expiresTime: "7d"
|
||||||
|
bufferTime: "1d"
|
||||||
|
issuer: "iotlcm"
|
||||||
|
|
||||||
|
# ---------- 数据库 ----------
|
||||||
|
# 建议为 OpenAPI 单独建只读账号,与 admin 的账号隔离。
|
||||||
|
database:
|
||||||
|
logger:
|
||||||
|
path: "/app/logs/sql"
|
||||||
|
level: "all"
|
||||||
|
stdout: true
|
||||||
|
default:
|
||||||
|
# 必改:连接串
|
||||||
|
link: "mysql:u_iotlcm:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
minIdle: 5
|
||||||
|
maxIdle: 30
|
||||||
|
maxActive: 5000
|
||||||
|
idleTimeout: "60s"
|
||||||
|
ClientName: "OpenAPICache"
|
||||||
|
|
||||||
|
# ---------- RabbitMQ ----------
|
||||||
|
rabbitmq:
|
||||||
|
enabled: true
|
||||||
|
dsn: "amqp://iotlcm:Iotlcm2026Default@rabbitmq:5672/"
|
||||||
|
|
||||||
|
# ---------- 免登录白名单 ----------
|
||||||
|
router:
|
||||||
|
exceptLogin:
|
||||||
|
- /openapi/v3/query/token
|
||||||
|
|
||||||
|
# ---------- 上游服务地址 ----------
|
||||||
|
upstream:
|
||||||
|
proxy:
|
||||||
|
address: "upstream-proxy:8510"
|
||||||
|
timeout: "10s"
|
||||||
|
gateway:
|
||||||
|
address: "upstream-chan:8110"
|
||||||
|
timeout: "10s"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-sms 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 服务商:1=阿里云,2=腾讯云
|
||||||
|
serviceType: "1"
|
||||||
|
|
||||||
|
# ---------- 必改:服务商凭据 ----------
|
||||||
|
regionId: cn-hangzhou
|
||||||
|
accessKeyId: ""
|
||||||
|
accessKeySecret: ""
|
||||||
|
|
||||||
|
# 短信签名,需已在服务商侧审核通过
|
||||||
|
signName: "示例签名"
|
||||||
|
|
||||||
|
# 仅腾讯云需要:
|
||||||
|
# sdkAppId: ""
|
||||||
|
|
||||||
|
# ---------- 场景模板库(可选) ----------
|
||||||
|
# 配置后按 scene 从 t_sms_template_config 表读取模板与签名,
|
||||||
|
# 改库即生效(短 TTL 缓存)。
|
||||||
|
# 不配置则 scene 功能关闭,调用方必须自带 templateId。
|
||||||
|
sceneDbDsn: "u_iotlcm:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?charset=utf8mb4&parseTime=true&loc=Local"
|
||||||
|
sceneDbCacheTtl: 60s
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-upstream-chan 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
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 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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: "iotlcm_diagnosis_2026"
|
||||||
|
|
||||||
|
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:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
minIdle: 5
|
||||||
|
maxIdle: 30
|
||||||
|
maxActive: 5000
|
||||||
|
idleTimeout: "60s"
|
||||||
|
ClientName: "SystemCache"
|
||||||
|
|
||||||
|
# ---------- 上游通道服务 ----------
|
||||||
|
gateway:
|
||||||
|
# 通道服务地址:必须带端口,只写服务名会走默认 80 端口被拒
|
||||||
|
address: "upstream-chan:8110"
|
||||||
|
timeout: 10s
|
||||||
|
# 单次批量转发的总预算上限。
|
||||||
|
# 非 APN 批量按「单卡超时 × 卡数」计算并以此封顶,
|
||||||
|
# 避免通道逐卡串行办理时请求仍在执行就被判失败
|
||||||
|
# (批量重启、停复机、批量查询)。
|
||||||
|
# worker 侧的超时配置必须大于该值。
|
||||||
|
batchTimeout: 20m
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# ============================================================
|
||||||
|
# iotlcm-worker-svr 配置
|
||||||
|
#
|
||||||
|
# 本文件随部署仓分发,已带可运行默认值,无需复制或改名。
|
||||||
|
# 正式上线前请修改口令等敏感项;修改时需与 docker-compose.yml、
|
||||||
|
# schema/init.sql 及 config/ 下其他文件保持一致(详见 README)。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
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:Iotlcm2026Default@tcp(mysql:3306)/db_iotlcm?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: "Iotlcm2026Default"
|
||||||
|
minIdle: 5
|
||||||
|
maxIdle: 30
|
||||||
|
maxActive: 5000
|
||||||
|
idleTimeout: "60s"
|
||||||
|
ClientName: "WorkerCache"
|
||||||
|
|
||||||
|
# ---------- RabbitMQ ----------
|
||||||
|
rabbitmq:
|
||||||
|
enabled: true
|
||||||
|
dsn: "amqp://iotlcm:Iotlcm2026Default@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"
|
||||||
|
# TDengine 3.x WebSocket 地址(容器内服务名 tdengine,端口 6041)
|
||||||
|
# 口令为 TDengine 默认 root/taosdata;改过口令需同步改这里
|
||||||
|
dsn: "root:taosdata@ws(tdengine:6041)/"
|
||||||
|
# dbName 只是连接池键名与兜底值,不决定实际访问哪个库
|
||||||
|
dbName: "iotlcm_daily"
|
||||||
|
dailyDbName: "iotlcm_daily"
|
||||||
|
monthlyDbName: "iotlcm_monthly"
|
||||||
|
maxOpenConns: 100
|
||||||
|
maxIdleConns: 10
|
||||||
|
|
||||||
|
# ---------- 上游服务地址 ----------
|
||||||
|
upstream:
|
||||||
|
proxy:
|
||||||
|
address: "upstream-proxy:8510"
|
||||||
|
# 异步提交协议版本,保持默认
|
||||||
|
apnAsyncProtocolVersion: 1
|
||||||
|
timeout: 10s
|
||||||
@@ -0,0 +1,431 @@
|
|||||||
|
name: iotlcm
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# IotLCM 单机部署编排
|
||||||
|
#
|
||||||
|
# 快速开始:
|
||||||
|
# 1. docker compose up -d
|
||||||
|
# 配置文件和初始口令均已内置;正式部署前按 README.md 修改默认口令。
|
||||||
|
#
|
||||||
|
# 首次启动会自动建库并导入 schema(见 schema/README.md)。
|
||||||
|
#
|
||||||
|
# 可选服务(默认不启动):
|
||||||
|
# docker compose --profile with-mock up -d # 运营商接口挡板
|
||||||
|
# docker compose --profile with-nginx up -d # 自带反向代理
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ---------------- 基础组件 ----------------
|
||||||
|
mysql:
|
||||||
|
# 基础组件统走同一镜像仓库(`<REGISTRY>/iotlcm/`),版本在此固定
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:mysql-8.0'
|
||||||
|
container_name: iotlcm-mysql
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 'Iotlcm2026Default'
|
||||||
|
MYSQL_DATABASE: 'db_iotlcm'
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./data/mysql:/var/lib/mysql
|
||||||
|
# 仅在 data/mysql 为空(首次启动)时执行 schema/init.sql
|
||||||
|
- ./schema:/docker-entrypoint-initdb.d:ro
|
||||||
|
command:
|
||||||
|
- --character-set-server=utf8mb4
|
||||||
|
- --collation-server=utf8mb4_general_ci
|
||||||
|
- --default-time-zone=+08:00
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "MYSQL_PWD=\"$$MYSQL_ROOT_PASSWORD\" mysqladmin ping -h 127.0.0.1 -u root --silent"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:redis-7.2'
|
||||||
|
container_name: iotlcm-redis
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
command:
|
||||||
|
- redis-server
|
||||||
|
- --requirepass
|
||||||
|
- 'Iotlcm2026Default'
|
||||||
|
- --appendonly
|
||||||
|
- "yes"
|
||||||
|
volumes:
|
||||||
|
- ./data/redis:/data
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "-a", "Iotlcm2026Default", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:rabbitmq-4.3.5-management'
|
||||||
|
container_name: iotlcm-rabbitmq
|
||||||
|
hostname: rabbitmq
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "5672:5672"
|
||||||
|
- "15672:15672"
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: 'iotlcm'
|
||||||
|
RABBITMQ_DEFAULT_PASS: 'Iotlcm2026Default'
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./data/rabbitmq:/var/lib/rabbitmq
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# 时序数据库:存流量历史(daily / monthly),worker-svr 全量同步写这里
|
||||||
|
tdengine:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:tdengine-3.3.6.13'
|
||||||
|
container_name: iotlcm-tdengine
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "${TDENGINE_PORT:-6041}:6041" # taosAdapter(WebSocket,应用用这个)
|
||||||
|
- "${TDENGINE_NATIVE_PORT:-6030}:6030"
|
||||||
|
environment:
|
||||||
|
TZ: '${TZ:-Asia/Shanghai}'
|
||||||
|
volumes:
|
||||||
|
- ./data/tdengine/data:/var/lib/taos
|
||||||
|
- ./data/tdengine/log:/var/log/taos
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -sf http://127.0.0.1:6041/-/ping || exit 1"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# 建库建表:业务代码不负责建 TDengine 库表,由本服务在 tdengine 健康后执行一次
|
||||||
|
tdengine-init:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:tdengine-3.3.6.13'
|
||||||
|
container_name: iotlcm-tdengine-init
|
||||||
|
restart: "no"
|
||||||
|
entrypoint: ["sh", "/init/run.sh"]
|
||||||
|
volumes:
|
||||||
|
- ./tdengine-init:/init:ro
|
||||||
|
depends_on:
|
||||||
|
tdengine:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
consul:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:consul-1.22'
|
||||||
|
container_name: iotlcm-consul
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8500:8500"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
CONSUL_LOCAL_CONFIG: |
|
||||||
|
{
|
||||||
|
"node_name": "iotlcm-node",
|
||||||
|
"bind_addr": "0.0.0.0",
|
||||||
|
"client_addr": "0.0.0.0",
|
||||||
|
"datacenter": "dc1",
|
||||||
|
"data_dir": "/consul/data",
|
||||||
|
"server": true,
|
||||||
|
"bootstrap_expect": 1,
|
||||||
|
"ui": true,
|
||||||
|
"connect": { "enabled": true }
|
||||||
|
}
|
||||||
|
volumes:
|
||||||
|
- ./data/consul:/consul/data
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
# ---------------- 业务服务 ----------------
|
||||||
|
admin-svr:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4'
|
||||||
|
container_name: iotlcm-admin-svr
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8008:8008"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/admin-svr:/app/config:ro
|
||||||
|
- ./data/logs/admin-svr:/app/logs
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
- ./data/upload:/app/resource/upload
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
admin-ui:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4'
|
||||||
|
container_name: iotlcm-admin-ui
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8009:80"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/admin-ui/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
- ./data/logs/admin-ui:/var/log/nginx
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
- admin-svr
|
||||||
|
|
||||||
|
openapi:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
|
||||||
|
container_name: iotlcm-openapi
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8000:8000"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/openapi:/app/config:ro
|
||||||
|
- ./data/logs/openapi:/app/logs
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
upstream-chan:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4'
|
||||||
|
container_name: iotlcm-upstream-chan
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8110:8110"
|
||||||
|
- "8310:8310"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/upstream-chan:/app/config:ro
|
||||||
|
- ./data/logs/upstream-chan:/app/logs
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
- ./data/upload:/app/resource/upload
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
consul:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
upstream-proxy:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4'
|
||||||
|
container_name: iotlcm-upstream-proxy
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8510:8510"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/upstream-proxy:/app/config:ro
|
||||||
|
- ./data/logs/upstream-proxy:/app/logs
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
- ./data/upload:/app/resource/upload
|
||||||
|
- ./data/tmp:/app/tmp
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
consul:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
worker-svr:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4'
|
||||||
|
container_name: iotlcm-worker-svr
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./config/worker-svr:/app/config:ro
|
||||||
|
- ./data/logs/worker-svr:/app/logs
|
||||||
|
- ./data/logs/worker-svr/full-sync:/app/logs/full-sync
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
- ./data/upload:/app/resource/upload
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
app-server:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-app-server-v1.0.2'
|
||||||
|
container_name: iotlcm-app-server
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8010:8010"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
APP_UPLOAD_DIR: /app/resource/upload
|
||||||
|
volumes:
|
||||||
|
- ./config/app-server:/app/config:ro
|
||||||
|
- ./data/upload:/app/resource/upload
|
||||||
|
- ./data/logs/app-server:/app/resource/log/server
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
app-web:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-app-web-v1.0.2'
|
||||||
|
container_name: iotlcm-app-web
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8018:80"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
volumes:
|
||||||
|
- ./data/logs/app-web:/var/log/nginx
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
sms:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-sms-v1.0.4'
|
||||||
|
container_name: iotlcm-sms
|
||||||
|
hostname: iotlcm-sms
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
HTTP_ADDR: ':11190'
|
||||||
|
GRPC_ADDR: ':11200'
|
||||||
|
ports:
|
||||||
|
- "11190:11190"
|
||||||
|
- "11200:11200"
|
||||||
|
volumes:
|
||||||
|
- ./config/sms:/app/config:ro
|
||||||
|
- ./data/logs/sms:/app/logs
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:11190/healthz"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
bot:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-bot-v1.0.4'
|
||||||
|
container_name: iotlcm-bot
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
command: ["watch", "--config", "/app/config/config.yaml"]
|
||||||
|
volumes:
|
||||||
|
- ./config/bot/config.yaml:/app/config/config.yaml:ro
|
||||||
|
- ./data/logs/worker-svr/full-sync:/data/full-sync:ro
|
||||||
|
- ./data/bot-state:/data/state
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
profiles:
|
||||||
|
- with-bot
|
||||||
|
|
||||||
|
docs:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-docs-v1.0.4'
|
||||||
|
container_name: iotlcm-docs
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8019:80"
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
# ---------------- 可选:运营商接口挡板 ----------------
|
||||||
|
# 模拟中国移动 OneLink 接口,用于在没有真实运营商环境时联调整条链路:
|
||||||
|
# admin-svr → worker-svr → upstream-chan → onelink-mock
|
||||||
|
#
|
||||||
|
# 启用:docker compose --profile with-mock up -d
|
||||||
|
# 启用后 upstream-chan 的 sim_yd_mock_v5.url 指向 http://onelink-mock:8080/v5/
|
||||||
|
# (config/upstream-chan 已预置该地址)
|
||||||
|
onelink-mock:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-onelink-mock-v1.0.4'
|
||||||
|
container_name: iotlcm-onelink-mock
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8080:8080"
|
||||||
|
environment:
|
||||||
|
TZ: 'Asia/Shanghai'
|
||||||
|
BAFFLE_ADDR: ':8080'
|
||||||
|
BAFFLE_SQLITE_PATH: /app/data/onelink_mock.db
|
||||||
|
volumes:
|
||||||
|
- ./data/mock:/app/data
|
||||||
|
- ./data/logs/mock:/app/logs
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:8080/api.json"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
profiles:
|
||||||
|
- with-mock
|
||||||
|
|
||||||
|
# ---------------- 可选:反向代理 ----------------
|
||||||
|
# 若宿主机已有 nginx / 面板,可不启动本容器,
|
||||||
|
# 参考 config/nginx/iotlcm.conf 把 location 段落到宿主机配置。
|
||||||
|
nginx:
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:nginx-1.30'
|
||||||
|
container_name: iotlcm-nginx
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./config/nginx:/etc/nginx/conf.d:ro
|
||||||
|
- ./data/logs/nginx:/var/log/nginx
|
||||||
|
- ./data/upload:/app/upload
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
depends_on:
|
||||||
|
- admin-ui
|
||||||
|
- openapi
|
||||||
|
- app-web
|
||||||
|
- docs
|
||||||
|
profiles:
|
||||||
|
- with-nginx
|
||||||
|
|
||||||
|
networks:
|
||||||
|
iotlcm-network:
|
||||||
|
driver: bridge
|
||||||
|
name: iotlcm-network
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# 数据库初始化
|
||||||
|
|
||||||
|
`init.sql` 是**单文件**初始化脚本,MySQL 容器首次以空数据卷启动时由 `docker-entrypoint-initdb.d` 自动执行。已有数据目录不会自动执行新增 DDL,升级时需先核对四张表状态,再按上游迁移规范处理,不能删除数据卷。
|
||||||
|
|
||||||
|
## 内容组成
|
||||||
|
|
||||||
|
按文件内顺序执行,整体幂等:
|
||||||
|
|
||||||
|
| 段 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 表结构与基础数据 | 建 `db_iotlcm` 库 + 全部表 + 菜单/字典/角色/租户/部门/演示账号 |
|
||||||
|
| 结构补齐 | `card_channel_assoc` 表 + 27 个列(用存储过程按 `information_schema` 判断) |
|
||||||
|
| 结构对齐生产 | `t_sms_template_config` 表、`batch_no` 默认值 |
|
||||||
|
| 通道模板 | 3 条(上游密钥已置空) |
|
||||||
|
| 数据字典 | 86 类 / 436 条 |
|
||||||
|
| Mock 通道 | `channel_yd_mock`。**卡全部挂在该通道上**,缺则卡片管理的通道列、筛选、开停复机均不可用 |
|
||||||
|
| 角色链 | 代理下发模板参数与角色下发标记 |
|
||||||
|
| 定时任务 | 20 条,**默认全部停用**(避免自动全量同步打满机器) |
|
||||||
|
| 菜单增量 | 达量断网按钮权限、全量同步页 |
|
||||||
|
| v1.0.4 前置 | 导出中心表 `t_export_task`、通知已读表 `sys_notice_user`、`card_channel_assoc.channel_id` 扩宽、菜单权限 |
|
||||||
|
| APN 异步恢复 | `data_operation_parent`、`data_operation_sub_batch`、`data_operation_sub_detail`、`data_operation_card_claim` 四表;须先建表后启动 worker |
|
||||||
|
| 数据修复 | `sys_dept.ancestors` 尾逗号归一化(全新库为空操作) |
|
||||||
|
| 业务账号与口令 | 建 `u_iotlcm` 账号、可选库 `iotdb`、种子账号口令统一重置 |
|
||||||
|
|
||||||
|
## 来源
|
||||||
|
|
||||||
|
内容取自**演示环境正在使用的种子库**,以生产 89 表基线为基础,补齐 v1.0.4 的 2 张表及 worker 异步恢复所需的 4 张表,初始化后为 **95 表**。四张恢复表直接采用内部数据库仓 `feat/data-operation-reconcile-20261015` 的 `SQL/20261015_data_operation_sub_batch.sql`(提交 `bf5f7fd`);原有 91 表统计不含这四张表。脱敏及部署适配包括:
|
||||||
|
|
||||||
|
- 演示账号昵称、邮箱、手机号、登录 IP、头像 → 占位或编造值
|
||||||
|
- 公司名、组织名 → 「示例企业」等通用名
|
||||||
|
- 内网/生产 IP、内部域名 → 删除或改为服务名
|
||||||
|
- 通道模板中真实上游密钥 → 置空
|
||||||
|
- 数据库账号与种子账号口令 → 统一为部署口令(字面值,见下)
|
||||||
|
|
||||||
|
## 改口令
|
||||||
|
|
||||||
|
口令是**字面值**,不使用环境变量。修改时必须同步以下三处:
|
||||||
|
|
||||||
|
| 位置 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| `docker-compose.yml` | `MYSQL_ROOT_PASSWORD`(MySQL 健康检查会引用此值) |
|
||||||
|
| `config/*/config.yaml` | 各服务 `database link` 的口令 |
|
||||||
|
| `schema/init.sql` 末段 | `u_iotlcm` 账号口令、种子账号重置口令 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OLD='原口令'; NEW='新口令'
|
||||||
|
grep -rl "$OLD" docker-compose.yml config/ schema/ | xargs sed -i "s/$OLD/$NEW/g"
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning 口令不能含 @ : / 等字符
|
||||||
|
连接串格式为 `mysql:用户:口令` 再接 `tcp(主机:端口)/库`,口令里的 `@`、`:`、`/` 会被当作分隔符,导致连接失败。用字母数字组合。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 重新初始化
|
||||||
|
|
||||||
|
`init.sql` 只在 `data/mysql` 为空时执行。改完必须清空数据卷才会重跑:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose stop mysql
|
||||||
|
sudo rm -rf ./data/mysql
|
||||||
|
docker compose up -d mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ 会删除全部数据,生产环境禁止执行。
|
||||||
|
|
||||||
|
## 空表说明
|
||||||
|
|
||||||
|
以下表为空属预期:
|
||||||
|
|
||||||
|
| 表 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `t_sim_flow_package_info` | 流量套餐。为空时无法办理套餐/续费/改套餐,需在管理端「套餐定价」中创建 |
|
||||||
|
| `t_sim_card` | 卡数据,可通过挡板服务造卡或自行入库 |
|
||||||
|
| 其余 `t_*` 业务表 | 随业务操作产生数据 |
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- TDengine 初始化:流量历史库与超级表
|
||||||
|
--
|
||||||
|
-- 结构取自生产环境(SHOW CREATE STABLE 导出),仅结构、无数据。
|
||||||
|
-- 由 compose 的 tdengine-init 服务在 tdengine 健康后执行。
|
||||||
|
--
|
||||||
|
-- 注意:`taos -f` 是**按行**执行语句的,每条 SQL 必须写在一行内,
|
||||||
|
-- 跨行会被判为 "Incomplete SQL statement"。改本文件时务必保持单行。
|
||||||
|
--
|
||||||
|
-- 业务代码(worker-svr / admin-svr)只连接读写,不负责建库建表,
|
||||||
|
-- 因此这一步必须在首次启动时完成,否则单卡流量重试写入会报库表不存在。
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
CREATE DATABASE IF NOT EXISTS iotlcm_daily PRECISION 'ms' KEEP 1095d DURATION 7d;
|
||||||
|
CREATE DATABASE IF NOT EXISTS iotlcm_monthly PRECISION 'ms' KEEP 1095d DURATION 30d;
|
||||||
|
CREATE STABLE IF NOT EXISTS iotlcm_daily.t_sim_card_usage_daily (ts TIMESTAMP, usage_mb DOUBLE, package_code VARCHAR(128)) TAGS (iccid VARCHAR(64));
|
||||||
|
CREATE STABLE IF NOT EXISTS iotlcm_monthly.t_sim_card_usage_monthly (ts TIMESTAMP, usage_mb DOUBLE, package_code VARCHAR(128)) TAGS (iccid VARCHAR(64));
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ============================================================
|
||||||
|
# TDengine 初始化执行脚本
|
||||||
|
#
|
||||||
|
# 由 compose 的 tdengine-init 服务调用,在 tdengine 健康后执行一次。
|
||||||
|
# 做三件事:
|
||||||
|
# 1. 过滤掉 init.sql 里的注释与空行(taos -f 会逐行执行,
|
||||||
|
# 注释行会被当成不完整 SQL 报错)
|
||||||
|
# 2. 执行建库建表
|
||||||
|
# 3. 校验超级表确实存在,缺表则以非零码退出(让 compose 暴露失败)
|
||||||
|
# ============================================================
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
echo "[tdengine-init] 准备 SQL(过滤注释与空行)"
|
||||||
|
grep -vE '^[[:space:]]*(--|$)' /init/init.sql > /tmp/init.sql
|
||||||
|
echo "[tdengine-init] 待执行语句数:$(wc -l < /tmp/init.sql)"
|
||||||
|
|
||||||
|
echo "[tdengine-init] 执行建库建表"
|
||||||
|
taos -h tdengine -f /tmp/init.sql
|
||||||
|
|
||||||
|
echo "[tdengine-init] 校验超级表"
|
||||||
|
missing=0
|
||||||
|
for pair in "iotlcm_daily:t_sim_card_usage_daily" "iotlcm_monthly:t_sim_card_usage_monthly"; do
|
||||||
|
db=${pair%%:*}
|
||||||
|
stable=${pair##*:}
|
||||||
|
if taos -h tdengine -s "show ${db}.stables;" 2>/dev/null | grep -q "$stable"; then
|
||||||
|
echo " OK ${db}.${stable}"
|
||||||
|
else
|
||||||
|
echo " MISS ${db}.${stable}"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$missing" -ne 0 ]; then
|
||||||
|
echo "[tdengine-init] 失败:超级表缺失,请检查 taos 输出"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[tdengine-init] 完成"
|
||||||
Reference in New Issue
Block a user