16 Commits

Author SHA1 Message Date
Dai0814 9f15ff6f9e revert: 撤掉单仓内的门户骨架,门户以独立仓 iotlcm-public/website 为唯一来源
mirror-to-gitee / mirror (push) Successful in 58s
原因
门户已拆分为独立仓 iotlcm-public/website(11:28 建仓,13:13 完成
fafc743「并入旧骨架独有价值内容,修 favicon 落地路径与过渡形态绑定」),
是门户的唯一事实来源。本仓 6c8314a 合并进来的 website/ 是拆分前的
旧骨架,继续保留会导致:
- 两份门户代码持续漂移,同一问题要在两个仓各修一遍
- 单仓 compose / gateway / Makefile 里残留 with-website profile 与
  /www/ 路由,与独立仓「门户自起容器、独立端口」的既定方案冲突

本提交 = revert -m 1 6c8314a,效果等同回到 e98f856
(git diff e98f856 HEAD 为空)。回退点:
tag backup/before-revert-portal-20261104 -> 6c8314a

门户部署改为(164 上,独立目录,不动 iotlcm-deploy):
  git clone https://git.5iot.com/iotlcm-public/website.git ~/iotlcm/website
  cd ~/iotlcm/website
  PORTAL_BIND_ADDR=0.0.0.0 docker compose -f deploy/docker-compose.yml up -d --build
2026-11-04 13:17:36 +08:00
Dai0814 6c8314a423 merge: 开源官网骨架接入(website/ + 部署编排 + 网关路由)
mirror-to-gitee / mirror (push) Successful in 1m13s
来源分支 feat/website-skeleton-20261104
含两个 404 修复:favicon 路径、文档中心链接前缀
2026-11-04 12:48:10 +08:00
Dai0814 9cd518227d fix(website): favicon 路径与文档站链接两处 404
实测发现两个问题,均导致线上 404:

1. favicon 不生效
   favicon.svg 放在 website/public/,但 srcDir 是 ./pages,
   VitePress 的 public 目录相对 srcDir 解析,即应为 pages/public/。
   原位置不被打包,每个页面的 /www/favicon.svg 全部 404。
   → 移到 website/pages/public/favicon.svg

2. 「文档中心」导航链接 404
   DOCS_URL 默认 '/docs/',VitePress 会给站内链接自动加 base 前缀,
   实际渲染成 '/www/docs/',而网关没有这个路径(文档站在 /docs/)。
   → 默认值改为完整 URL,VitePress 判定为外链不做前缀拼接

验证(SITE_BASE=/www/ 与 / 两形态)
- 页面引用完整性:146 → 139 条,失效 15 → 0
- HTTP 实测(模拟 nginx alias + try_files):8 页 200、
  CSS/JS/favicon 200、/www 301、不存在页 404
2026-11-04 12:33:08 +08:00
技小云 d78379486c feat: 新增开源版官网骨架 website/,并接入部署编排与网关路由
- website/:VitePress 官网工程(首页/功能/对比/快速开始/部署运维/教程/社区),与文档站同构
- 两种形态由 SITE_BASE 决定:/www/ 子路径(过渡)与 / 根路径(独立域名),内容同源
- deploy/:新增 with-website profile、网关 /www/ 路由、多域名 website vhost 段
- Makefile:新增 website 镜像条目与 make website 目标
- 教程页给出部署/使用两支视频的元数据与录制要求,官网留有教程入口
2026-11-04 10:41:05 +08:00
MiFaZhan e98f8561f7 fix(deploy): nginx 上游运行期重新解析 + 就绪探针;内存上限按 8C/32G 重新分配
mirror-to-gitee / mirror (push) Successful in 9s
问题:容器重建换 IP 后,网关与 admin-ui 仍往旧 IP 打,/openapi 与 /api
整片 502,必须手动重启 nginx 容器才恢复(2026-11-03 实机踩到)。

编排
- admin-svr / openapi / admin-ui / docs 增加 healthcheck
- admin-ui、gateway、nginx 改 depends_on: condition: service_healthy,
  保证 nginx 启动时后端已就绪
- 新增 deploy/compose.memlimit.yml(原来只存在于某台演示机上):
  全套上限 8.5G、实测常驻 2G,留给同机其它服务 20G+;
  MySQL buffer pool 128M→768M、max_connections 151→300

nginx
- gateway / admin-ui / iotlcm.conf 示例 统一改 resolver 127.0.0.11 valid=10s
  + upstream zone/resolve:运行期跟随 IP 变化,不必重启(实测 6s 内自愈)
2026-11-03 10:40:48 +08:00
MiFaZhan 70749bea7a docs: README 手册入口改为公网地址(open.5iot.com:5051)
mirror-to-gitee / mirror (push) Has been cancelled
使用手册(Web / 小程序)、部署手册、OpenAPI 合同、共享 Token 全部指向
在线文档站,附文档源码位置(docs/)与自建实例的 8019 访问方式。
2026-11-03 09:30:23 +08:00
MiFaZhan fd42b5976e docs: 重写 README(用户视角,含手册跳转)+ 去掉「开源版」措辞 + 单端口入口 5051
mirror-to-gitee / mirror (push) Successful in 4s
trigger-jenkins-oss / trigger (push) Successful in 0s
README
- 标题去掉「(开源版)」:对使用者就是 iotLCM
- 新增「文档」表:使用手册(Web 端 / 小程序端)、部署手册、OpenAPI 合同、
  共享 Token 接口,全部指向仓内 docs/ 路径;并给出部署后的文档站地址
- 补功能概览、服务构成(带镜像 tag)、从源码构建、版本与镜像、许可

去措辞
- Gitee 建仓描述同步去掉「(开源版)」

单端口入口(新增,对外默认 5051)
- deploy/config/gateway/nginx.conf:/ → 管理端、/docs/ → 文档站、
  /openapi/ 与 /route/open/ → openapi
- compose 增加 gateway 服务(profile with-gateway),端口由 GATEWAY_PORT 控制
  实测:/ 200、/docs/ 200(含静态资源)、/api/agent/dept/list 200、
  /api/system/auth/login 返回后端真实 JSON
- nginx profile 的 80 端口改为可配 NGINX_HTTP_PORT(文档早有提及但 compose 写死)
- 部署手册概览页修正过期内容(schema 单文件、服务清单补全)、optional 增单端口入口段
2026-11-02 14:57:57 +08:00
MiFaZhan a7c15cf891 chore: 移除一次性探测/清理工作流(Gitee 删除需 projects 权限,API 侧 403)
mirror-to-gitee / mirror (push) Successful in 4s
2026-11-02 14:25:34 +08:00
MiFaZhan 2548976f73 chore: 探测 Gitee 删除端点(一次性)
mirror-to-gitee / mirror (push) Successful in 5s
2026-11-02 14:24:18 +08:00
如是 & 技小云 17c839daac fix(deploy): Mock 通道凭据对齐挡板默认值
mirror-to-gitee / mirror (push) Successful in 5s
schema/init.sql 里 sim_yd_mock_v5 通道的 app_key/app_secret 仍是内网挡板的老值(100059999270000006 / 老口令),与开源版挡板 onelink-mock 的默认凭据(demo_appid / demo_password,见 onelink-mock/internal/service/store.go 的 defaultStore())不一致,导致 chan 侧向挡板换 Token 失败。

改为挡板当前实际默认值,并在注释里标注取值来源。
2026-11-02 14:23:03 +08:00
MiFaZhan f76e988689 chore: 清理工作流改为 artifact 回报(Gitea 无日志读取 API)
mirror-to-gitee / mirror (push) Successful in 7s
2026-11-02 14:22:00 +08:00
MiFaZhan 1634d27a43 chore: 一次性清理工作流(删除 Gitee 侧 12 个已并入单仓的旧仓)
mirror-to-gitee / mirror (push) Successful in 5s
删除后会再删掉本文件。
2026-11-02 14:20:13 +08:00
MiFaZhan 42d12d905a chore: 小程序线(app)固定 v1.0.2,不跟随平台 tag
mirror-to-gitee / mirror (push) Successful in 5s
版本号与功能一一对应:小程序线功能停在 v1.0.2,镜像 tag 也停在 v1.0.2,
平台后续发 v1.0.5 时它不动。

- deploy/docker-compose.yml:app-server / app-web 两行回退 v1.0.2
- Makefile:IMAGE_MAP 增加第 5 列「固定版本」,app 两行填 v1.0.2,
  本地构建与 CI 行为一致
- 根 README:新增「小程序线是独立版本线」说明
- app/README.md:标注本目录对应 v1.0.2,不跟随平台 tag
- 文档:deploy/README 版本表、build-image 的 tag 表与说明、
  offline / prerequisites / configuration 示例 tag 全部回退 v1.0.2

CI 侧由 Jenkins 流水线的 PIN 表(app|v1.0.2)保证:tag 构建自动跳过 app,
需要重建时单独触发 SERVICES=app VERSION=v1.0.2。
2026-11-02 14:11:13 +08:00
MiFaZhan 89a15c4fca chore: make help 只列命令,不再多印配置行
mirror-to-gitee / mirror (push) Successful in 4s
2026-11-02 13:59:47 +08:00
MiFaZhan 34669c9e1f chore: 镜像校验覆盖 tag 集合
mirror-to-gitee / mirror (push) Successful in 4s
推送后除比对 main 的 sha,还比对本地与 Gitee 端的 tag 集合,
不一致即判失败(Gitee 限流时容易出现“推送成功但 tag 丢失”)。
2026-11-02 13:55:45 +08:00
MiFaZhan 12512b06ff fix: 单仓后统一版本号与访问地址说明
mirror-to-gitee / mirror (push) Successful in 4s
trigger-jenkins-oss / trigger (push) Successful in 0s
- 编排与文档里的 app 镜像 tag 由 v1.0.2 统一为 v1.0.4(单仓一个发布版本,
  v1.0.4 包含 app 线,不再保留源仓的历史 tag 差异)
- deploy/README.md、docs/deploy/install.md 明确业务端口默认只绑 127.0.0.1,
  并给出对外访问的两种做法(with-nginx profile / 改绑定)
- mirror-to-gitee 增加推送后校验:远端 main 与本地不一致则判失败
2026-11-02 13:40:26 +08:00
15 changed files with 541 additions and 87 deletions
+22 -1
View File
@@ -47,7 +47,7 @@ jobs:
curl -sS -X POST \ curl -sS -X POST \
"https://gitee.com/api/v5/enterprises/${DST_OWNER}/repos?access_token=${DST_TOKEN}" \ "https://gitee.com/api/v5/enterprises/${DST_OWNER}/repos?access_token=${DST_TOKEN}" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d "{\"name\":\"${REPO_NAME}\",\"auto_init\":false,\"description\":\"iotLCM 物联网卡全生命周期管理平台(开源版)\"}" \ -d "{\"name\":\"${REPO_NAME}\",\"auto_init\":false,\"description\":\"iotLCM 物联网卡全生命周期管理平台\"}" \
-o /tmp/repo-create.json -w '建仓 HTTP %{http_code}\n' || true -o /tmp/repo-create.json -w '建仓 HTTP %{http_code}\n' || true
head -c 300 /tmp/repo-create.json 2>/dev/null; echo head -c 300 /tmp/repo-create.json 2>/dev/null; echo
else else
@@ -69,6 +69,27 @@ jobs:
echo "----- 推送尝试 $attempt/5" echo "----- 推送尝试 $attempt/5"
if git push --mirror "https://${DST_USER}:${DST_TOKEN}@${DST}" 2>&1; then if git push --mirror "https://${DST_USER}:${DST_TOKEN}@${DST}" 2>&1; then
echo "✅ 已同步到 Gitee" echo "✅ 已同步到 Gitee"
# 推送后校验:远端 main 与 tag 集合必须与本地一致
# (避免“绿了但其实没推上去” —— Gitee 限流时容易发生)
LOCAL_MAIN=$(git rev-parse refs/heads/main 2>/dev/null || true)
REMOTE_MAIN=$(git ls-remote "https://${DST_USER}:${DST_TOKEN}@${DST}" refs/heads/main | awk '{print $1}')
LOCAL_TAGS=$(git for-each-ref --format='%(refname:short)' refs/tags | sort | tr '\n' ' ')
REMOTE_TAGS=$(git ls-remote --tags "https://${DST_USER}:${DST_TOKEN}@${DST}" \
| awk '{print $2}' | sed 's#refs/tags/##' | grep -v '\^{}$' | sort | tr '\n' ' ')
echo "本地 main: ${LOCAL_MAIN:-<无>}"
echo "远端 main: ${REMOTE_MAIN:-<无>}"
echo "本地 tag: ${LOCAL_TAGS:-<无>}"
echo "远端 tag: ${REMOTE_TAGS:-<无>}"
if [ -n "$LOCAL_MAIN" ] && [ "$LOCAL_MAIN" != "$REMOTE_MAIN" ]; then
echo "❌ 校验失败:远端 main 与本地不一致"
exit 1
fi
if [ "${LOCAL_TAGS% }" != "${REMOTE_TAGS% }" ]; then
echo "❌ 校验失败:远端 tag 与本地不一致"
exit 1
fi
echo "✅ 校验通过:Gitee 端 main 与 tag 均已对齐"
exit 0 exit 0
fi fi
wait_s=$((attempt * 30)) wait_s=$((attempt * 30))
+20 -18
View File
@@ -15,25 +15,26 @@ BASE_REGISTRY ?= docker.io/library
GO_MODULES := admin-svr/server app/server bot onelink-mock openapi sms upstream-chan upstream-proxy worker-svr GO_MODULES := admin-svr/server app/server bot onelink-mock openapi sms upstream-chan upstream-proxy worker-svr
# 目录|镜像名|构建上下文|Dockerfile # 目录|镜像名|构建上下文|Dockerfile|固定版本(留空=跟随 VERSION)
# 小程序线 app 固定 v1.0.2:版本号与功能一一对应,平台发新 tag 时不跟着动。
IMAGE_MAP := \ IMAGE_MAP := \
admin-svr|iotlcm-admin-svr|admin-svr/server|build/Dockerfile \ admin-svr|iotlcm-admin-svr|admin-svr/server|build/Dockerfile| \
admin-ui|iotlcm-admin-ui|admin-ui|vue/build/Dockerfile \ admin-ui|iotlcm-admin-ui|admin-ui|vue/build/Dockerfile| \
openapi|iotlcm-openapi|openapi|build/Dockerfile \ openapi|iotlcm-openapi|openapi|build/Dockerfile| \
worker-svr|iotlcm-worker-svr|worker-svr|build/Dockerfile \ worker-svr|iotlcm-worker-svr|worker-svr|build/Dockerfile| \
upstream-chan|iotlcm-upstream-chan|upstream-chan|build/Dockerfile \ upstream-chan|iotlcm-upstream-chan|upstream-chan|build/Dockerfile| \
upstream-proxy|iotlcm-upstream-proxy|upstream-proxy|build/Dockerfile \ upstream-proxy|iotlcm-upstream-proxy|upstream-proxy|build/Dockerfile| \
app|iotlcm-app-server|app/server|Dockerfile \ app|iotlcm-app-server|app/server|Dockerfile|v1.0.2 \
app|iotlcm-app-web|app/app|Dockerfile \ app|iotlcm-app-web|app/app|Dockerfile|v1.0.2 \
sms|iotlcm-sms|sms|build/Dockerfile \ sms|iotlcm-sms|sms|build/Dockerfile| \
bot|iotlcm-bot|bot|Dockerfile \ bot|iotlcm-bot|bot|Dockerfile| \
docs|iotlcm-docs|docs|Dockerfile \ docs|iotlcm-docs|docs|Dockerfile| \
onelink-mock|iotlcm-onelink-mock|onelink-mock|Dockerfile onelink-mock|iotlcm-onelink-mock|onelink-mock|Dockerfile|
.PHONY: help build test images docs clean .PHONY: help build test images docs clean
help: help:
@sed -n '2,12p' Makefile | sed 's/^# \{0,1\}//' @grep -E '^# make' Makefile | sed 's/^# //'
build: build:
@set -e; for m in $(GO_MODULES); do \ @set -e; for m in $(GO_MODULES); do \
@@ -50,15 +51,16 @@ test:
images: images:
@set -e; echo "镜像前缀: $(REGISTRY) 版本: $(VERSION)"; \ @set -e; echo "镜像前缀: $(REGISTRY) 版本: $(VERSION)"; \
echo "$(IMAGE_MAP)" | tr ' ' '\n' | while IFS='|' read -r dir img ctx df; do \ echo "$(IMAGE_MAP)" | tr ' ' '\n' | while IFS='|' read -r dir img ctx df pin; do \
[ -z "$$dir" ] && continue; \ [ -z "$$dir" ] && continue; \
echo ""; echo "==> $$img:$$VERSION (context=$$ctx dockerfile=$$df)"; \ v="$${pin:-$(VERSION)}"; \
echo ""; echo "==> $$img:$$v (context=$$ctx dockerfile=$$df$${pin:+ [固定版本]})"; \
docker build -f "$$ctx/$$df" \ docker build -f "$$ctx/$$df" \
--build-arg BASE_REGISTRY="$(BASE_REGISTRY)" \ --build-arg BASE_REGISTRY="$(BASE_REGISTRY)" \
--build-arg BuildVersion="$(VERSION)" \ --build-arg BuildVersion="$$v" \
--build-arg CommitID="$$(git rev-parse --short=7 HEAD)" \ --build-arg CommitID="$$(git rev-parse --short=7 HEAD)" \
--build-arg BuildTime="$$(date -u +%FT%TZ)" \ --build-arg BuildTime="$$(date -u +%FT%TZ)" \
-t "$(REGISTRY):$$img-$(VERSION)" "$$ctx"; \ -t "$(REGISTRY):$$img-$$v" "$$ctx"; \
done done
docs: docs:
+69 -37
View File
@@ -1,51 +1,76 @@
# iotLCM —— 物联网卡全生命周期管理平台(开源版) # iotLCM 物联网卡全生命周期管理平台
一站式物联网 SIM 卡管理平台:卡片从运营商渠道入库 → 划拨给代理商 → 激活 / 续费 / 停机 → 流量与状态同步 → 销户。对接中国移动 OneLink、中国电信 5G CMP、中国联通 10646 等运营商接口,并通过共享 Token 模式接入多家三方渠道商。 iotLCM 面向物联网 SIM 卡的全生命周期管理:卡片从运营商渠道入库 → 划拨给代理商 → 激活 / 续费 / 停机 → 流量与状态同步 → 销户。
**单仓多服务**:平台全部后端服务、前端应用、文档站与部署编排都在本仓,一次 clone 即可获得完整源码。 平台对接**中国移动 OneLink**、**中国电信 5G CMP**、**中国联通 10646** 等运营商接口,支持以共享 Token 模式接入三方渠道商,并提供对外的 OpenAPI 服务。
## 目录 ## 文档
| 目录 | 说明 | 镜像 | | 手册 | 内容 | 入口 |
| --- | --- | --- | | --- | --- | --- |
| `admin-svr/` | 管理端后端(GoFrame):卡信息、通信管理、资费、告警工单、统计、账号治理 | `iotlcm-admin-svr` | | **使用手册 · Web 管理端** | 查卡、智能诊断、业务受理、套餐与定价、库存出入库、通道管理、企业用户与角色、工单、API 凭证、常见问题 | <http://open.5iot.com:5051/docs/manual/web/> |
| `admin-ui/` | 管理端前端(Vue 3 + Vben Admin) | `iotlcm-admin-ui` | | **使用手册 · 小程序端** | 首页公开查卡、卡片排查、客户管理与划拨、工单、批次执行日志、消息与个人中心 | <http://open.5iot.com:5051/docs/manual/mini-program/> |
| `openapi/` | 对外 API 网关:V3 接口、旧版兼容、运营商能力代理 | `iotlcm-openapi` | | **部署手册** | 推荐环境、一键部署、配置说明、离线部署、拆分部署、自建镜像、运维与排错 | <http://open.5iot.com:5051/docs/deploy/> |
| `worker-svr/` | 异步执行:RabbitMQ 消费者、全量同步、批量任务 | `iotlcm-worker-svr` | | **OpenAPI 合同** | 鉴权、V3 接口、旧版兼容接口、运营商能力代理 | <http://open.5iot.com:5051/docs/openapi/> |
| `upstream-chan/` | 运营商 / 三方渠道对接层(OneLink、CMP、10646、共享 Token) | `iotlcm-upstream-chan` | | **共享 Token 接口** | 共享 Token 获取与白名单转发 | <http://open.5iot.com:5051/docs/openapi/operator/> |
| `upstream-proxy/` | 通道缓存转发层(挡在 chan 前面的本地缓存) | `iotlcm-upstream-proxy` |
| `app/server/` | 用户端服务(GoFrame) | `iotlcm-app-server` |
| `app/app/` | 用户端前端(uni-app,基于 unibest,MIT) | `iotlcm-app-web` |
| `sms/` | 短信 HTTP / gRPC 服务(阿里云、腾讯云) | `iotlcm-sms` |
| `bot/` | 飞书通知机器人(运维自用,可选) | `iotlcm-bot` |
| `docs/` | 文档站(VitePress):使用手册 + OpenAPI 合同 + 部署手册 | `iotlcm-docs` |
| `onelink-mock/` | OneLink 接口挡板(联调 / 演示,可选) | `iotlcm-onelink-mock` |
| `deploy/` | Docker Compose 编排与数据库初始化 | 参考 `deploy/README.md` |
每个子目录都有自己的 `README.md`,说明该服务的职责、配置项与本地运行方式。 在线文档站:**<http://open.5iot.com:5051/docs/>**。文档源码在本仓 [`docs/`](docs) 目录,随平台一起发布(自建实例访问 `http://<平台地址>:8019/docs/`)。
## 快速开始(只跑起来) ## 快速开始
不需要编译,直接用预构建镜像拉起整栈: 需要 Docker 20.10+ 与 Docker Compose v2,单机 4 核 / 8 GB 起。预构建镜像已就绪,不需要编译:
```bash ```bash
git clone <本仓库地址> iotlcm git clone <仓库地址> iotlcm
cd iotlcm/deploy cd iotlcm/deploy
docker compose up -d docker compose up -d
``` ```
首次启动自动完成 MySQL 建库建表(95 张)、种子数据导入、时序库建表,实测 30 秒 ~ 1 分钟。 首次启动自动完成建库建表、种子数据导入与时序库建表,约 1 分钟。启动完成后访问 `http://127.0.0.1:8009`。
默认账号 `admin`,口令见 `deploy/README.md`(**正式使用前必须修改**)。详细的端口、配置项、可选服务(挡板 / 飞书通知 / 反向代理)、离线部署与排错,见 `deploy/README.md` 与文档站 `docs/deploy/`。 默认账号口令、端口清单、可选服务(接口挡板 / 飞书通知 / 反向代理)、修改口令与上线检查,见 [`deploy/README.md`](deploy/README.md) 与[部署手册](http://open.5iot.com:5051/docs/deploy/)。
> 对外端口默认只监听 `127.0.0.1`,需要局域网或公网访问时,用 `deploy/` 里的 nginx 编排改绑定地址,或并入宿主机已有的反向代理。 > 业务端口默认只绑定 `127.0.0.1`。需要局域网或公网访问时,用 `--profile with-nginx` 起反向代理,或改用 `--profile with-gateway` 的单端口入口(默认 5051)。
## 功能概览
| 域 | 能力 |
| --- | --- |
| 卡资产 | 卡信息、批量导入、入库 / 库存 / 出库、批量开停复机、续费、变更套餐、达量断网 |
| 通信管理 | 通道与接入模板、运营商能力对接、共享 Token 接入三方渠道、通道缓存转发 |
| 资费 | 套餐与流量池、定价、生效与到期策略 |
| 代理商 | 代理商与分佣、划拨与回收 |
| 数据同步 | 流量 / 状态 / IMEI 全量同步与增量对齐、历史账期时序数据 |
| OpenAPI | V3 接口、旧版兼容接口、运营商能力代理(白名单转发 / 共享 Token) |
| 运营 | 工单、预警、执行日志、审计、智能诊断 |
| 端 | Web 管理端、小程序 / H5 用户端 |
## 服务构成
| 目录 | 说明 | 镜像 tag |
| --- | --- | --- |
| `admin-svr/` | 管理端后端(GoFrame) | `iotlcm-admin-svr` |
| `admin-ui/` | 管理端前端(Vue 3 + Vben Admin) | `iotlcm-admin-ui` |
| `openapi/` | 对外 API 网关 | `iotlcm-openapi` |
| `worker-svr/` | 异步执行(RabbitMQ 消费者、全量同步、批量任务) | `iotlcm-worker-svr` |
| `upstream-chan/` | 运营商 / 三方渠道对接层 | `iotlcm-upstream-chan` |
| `upstream-proxy/` | 通道缓存转发层 | `iotlcm-upstream-proxy` |
| `app/server/` | 用户端服务(GoFrame) | `iotlcm-app-server` |
| `app/app/` | 用户端前端(uni-app,基于 unibest) | `iotlcm-app-web` |
| `sms/` | 短信服务(HTTP / gRPC,阿里云 / 腾讯云) | `iotlcm-sms` |
| `bot/` | 飞书通知机器人(运维自用,可选) | `iotlcm-bot` |
| `docs/` | 文档站(VitePress)与 OpenAPI 合同 | `iotlcm-docs` |
| `onelink-mock/` | OneLink 接口挡板(联调 / 演示,可选) | `iotlcm-onelink-mock` |
| `deploy/` | Docker Compose 编排、数据库初始化、反向代理 / 单端口网关配置 | — |
各服务职责、配置项与本地运行方式见各自目录下的 `README.md`。
## 从源码构建 ## 从源码构建
每个 Go 服务是独立模块(各自带 `go.mod`),在对应目录内构建: 每个 Go 服务是独立模块,在对应目录内构建:
```bash ```bash
cd worker-svr && go build -o out/ . # 其它 Go 服务同理 cd worker-svr && go build -o out/ . # 其余 Go 服务同理
``` ```
前端: 前端:
@@ -56,31 +81,38 @@ cd app/app && pnpm install && pnpm build
cd docs && npm ci && npm run build cd docs && npm ci && npm run build
``` ```
或一键编译检查全部 Go 模块: 一键编译全部 Go 服务 / 本地构建全部镜像:
```bash ```bash
make build # 所有 Go 服务编译 make build # 编译检查
make images # 本地构建全部镜像 make images # 构建镜像(REGISTRY / VERSION 可覆盖)
``` ```
构建镜像所需的 Dockerfile 位置与构建上下文,见 `docs/docs/deploy/build-image.md`。 各服务的 Dockerfile 位置与构建上下文见[自建镜像](http://open.5iot.com:5051/docs/deploy/build-image)。
## 发布与镜像 ## 版本与镜像
所有镜像发布到**同一个镜像仓库**,用 tag 区分服务与版本: 镜像统一发布在同一个仓库,用 tag 区分服务与版本:
```text ```text
<镜像仓库>/iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4 <镜像仓库>/iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4
└─ 仓库 ┘ └─ 服务名 ─┘└版本┘ └─ 仓库 ┘ └─ 服务名 ─┘└版本┘
``` ```
打 `v*` tag 会经由内网 CI 构建全部镜像并推送,见 `.gitea/workflows/`。 | 服务 | 当前版本 |
| --- | --- |
| 除小程序线外 | `v1.0.4` |
| `iotlcm-app-server` / `iotlcm-app-web` | `v1.0.2`(小程序线独立版本线) |
升级时固定到具体版本,不要使用 `latest`。
## 反馈
问题与建议请提 Issue,或联系平台维护者。
## 许可 ## 许可
本仓整体采用 [Apache License 2.0](LICENSE)。 本仓整体采用 [Apache License 2.0](LICENSE)。以下子目录基于上游 MIT 项目衍生,保留其原始许可:
以下子目录基于上游 MIT 项目衍生,保留其原始许可:
| 目录 | 上游 | 许可 | | 目录 | 上游 | 许可 |
| --- | --- | --- | | --- | --- | --- |
+3
View File
@@ -2,6 +2,9 @@
iotLCM 物联网卡全生命周期管理平台的**小程序端 + 用户端服务**。 iotLCM 物联网卡全生命周期管理平台的**小程序端 + 用户端服务**。
> **版本线独立**:本目录对应小程序线的 **v1.0.2**,不跟随平台 tag(平台当前为 v1.0.4)。
> 功能变更时才提升本线版本,镜像 tag 仍为 `iotlcm-app-server-<版本>` / `iotlcm-app-web-<版本>`。
- `app/` —— uni-app 小程序前端(基于 [unibest](https://github.com/unibest-tech/unibest),MIT) - `app/` —— uni-app 小程序前端(基于 [unibest](https://github.com/unibest-tech/unibest),MIT)
- `server/` —— 用户端 GoFrame 服务(基于 XiuAdmin 框架,Apache-2.0) - `server/` —— 用户端 GoFrame 服务(基于 XiuAdmin 框架,Apache-2.0)
+36 -7
View File
@@ -18,7 +18,7 @@ docker compose up -d
docker compose logs -f mysql docker compose logs -f mysql
``` ```
启动完成后访问 `http://<宿主机IP>:8009`: 启动完成后在本机访问 `http://127.0.0.1:8009`(编排默认把业务端口绑定在 127.0.0.1,需要局域网/公网访问见下方「重要提示」):
| 项 | 值 | | 项 | 值 |
| --- | --- | | --- | --- |
@@ -30,12 +30,32 @@ docker compose logs -f mysql
## 可选服务 ## 可选服务
```bash ```bash
docker compose --profile with-mock up -d # 运营商接口挡板 docker compose --profile with-gateway up -d # 单端口入口(默认对外 5051)
docker compose --profile with-bot up -d # 飞书通知(运维自用) docker compose --profile with-mock up -d # 运营商接口挡板
docker compose --profile with-nginx up -d # 自带反向代理 docker compose --profile with-bot up -d # 飞书通知(运维自用)
docker compose --profile with-nginx up -d # 多域名反向代理(对外 80)
``` ```
多个 profile 可组合:`--profile with-mock --profile with-nginx up -d` 多个 profile 可组合:`--profile with-gateway --profile with-mock up -d`
### 单端口入口(with-gateway)
只暴露一个端口时用它。容器内监听 80,宿主机端口由 `GATEWAY_PORT` 控制(默认 **5051**):
```bash
docker compose --profile with-gateway up -d
# 或换端口
GATEWAY_PORT=8088 docker compose --profile with-gateway up -d
```
| 路径 | 去向 |
| --- | --- |
| `/` | 管理端(admin-ui,内部已代理 `/api/` 到 admin-svr) |
| `/docs/` | 文档站 |
| `/openapi/` | 对外 API |
| `/route/open/` | 运营商能力代理(白名单 / 共享 Token) |
配置见 `config/gateway/nginx.conf`。小程序 / H5 端的静态资源是根路径部署的,不能挂在同一域名的子路径下,需要单独域名或端口(见 `with-nginx` 的 app 段)。
`with-bot` 是把全量同步等运维事件推送到飞书群的**内部运维工具**,不是平台功能。它需要你自己的飞书自建应用凭证(`config/bot/config.yaml` 的 `appId` / `appSecret` / `chatId`),没填就不启动。 `with-bot` 是把全量同步等运维事件推送到飞书群的**内部运维工具**,不是平台功能。它需要你自己的飞书自建应用凭证(`config/bot/config.yaml` 的 `appId` / `appSecret` / `chatId`),没填就不启动。
@@ -54,7 +74,14 @@ crpi-.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2
tag 格式统一为 **`<服务名>-<版本>`**。 tag 格式统一为 **`<服务名>-<版本>`**。
当前业务版本:大部分服务为 `v1.0.4`,`iotlcm-app-server` / `iotlcm-app-web` 为 `v1.0.2`(源仓最新 tag 就是 v1.0.2)。 当前业务版本:
| 服务 | 版本 |
| --- | --- |
| 除 app 线外的全部业务服务 | `v1.0.4` |
| `iotlcm-app-server` / `iotlcm-app-web` | **`v1.0.2`**(小程序线独立版本,不跟随平台 tag) |
> 版本号与功能一一对应:小程序线的功能停在 v1.0.2,因此镜像 tag 也停在 v1.0.2;平台后续发 v1.0.5 时它依然不动。
升级时在 `docker-compose.yml` 中同步更新 tag 的版本部分,不要改用 `latest`。 升级时在 `docker-compose.yml` 中同步更新 tag 的版本部分,不要改用 `latest`。
@@ -122,7 +149,9 @@ TDengine 版本为 `3.3.6.13`(公开镜像可得),生产用的是 `3.4.0.9
- **`JWT_SIGNING_KEY` 必须在 `admin-svr`、`openapi`、`app-server` 三者间完全一致**,否则令牌跨服务不通 - **`JWT_SIGNING_KEY` 必须在 `admin-svr`、`openapi`、`app-server` 三者间完全一致**,否则令牌跨服务不通
- **镜像标签不要用 `latest`**,生产环境请固定到具体版本 - **镜像标签不要用 `latest`**,生产环境请固定到具体版本
- **改 `schema/` 下的脚本不会自动生效**,需要清空 `data/mysql` 重新初始化(会丢失全部数据) - **改 `schema/` 下的脚本不会自动生效**,需要清空 `data/mysql` 重新初始化(会丢失全部数据)
- 默认只监听 `127.0.0.1` 的服务需通过反向代理对外 - 业务端口默认只绑定 `127.0.0.1`(admin-ui/admin-svr/openapi/app-web/docs/sms),要从其他机器访问,二选一:
- 用 `--profile with-nginx` 起自带反向代理(对外 80,按域名/路径分流,配置见 `config/nginx/iotlcm.conf`)
- 或把 `docker-compose.yml` 里对应服务的 `127.0.0.1:xxxx` 改成 `0.0.0.0:xxxx`(需自备防火墙策略)
## 许可 ## 许可
+175
View File
@@ -0,0 +1,175 @@
# ============================================================
# iotlcm 内存上限 override —— 8 vCPU / 32 GB 宿主机,与其他服务共用
#
# 定位:这台机器是「一机多环境」,iotlcm 只是其中一个占用方。
# 所以只给全套 20 个服务留 **约 8.5 GB 上限**(实测常驻约 2 GB),
# 剩余 20+ GB 留给宿主机上要新增的其它服务。
#
# 没有本文件会怎样:编排里没有内存限制,任何一个服务泄漏/跑批都会
# 把整机内存吃到 OOM Killer 随机杀进程(表现为容器反复 Restarting
# 且日志里看不到业务异常,只能 dmesg 里看到 oom-kill)。
#
# 用法(顺序不能反):
# docker compose -f docker-compose.yml -f compose.memlimit.yml up -d
#
# 只调 resources(+ MySQL 自身缓存参数),不动镜像、端口、卷、依赖。
# 换成更小的机器(4 GB)时按比例下调:mysql 1024M / tdengine 512M /
# worker-svr 768M / 其余 320M;8 GB 机器可用 1024M / 768M / 512M。
# ============================================================
services:
# ---------- 基础组件 ----------
mysql:
# buffer pool 由 128M(默认)提到 768M,连接数 151 → 300
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_general_ci
- --default-time-zone=+08:00
- --innodb-buffer-pool-size=768M
- --max-connections=300
deploy:
resources:
limits:
memory: 2048M
reservations:
memory: 768M
redis:
deploy:
resources:
limits:
memory: 384M
reservations:
memory: 96M
rabbitmq:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 192M
tdengine:
deploy:
resources:
limits:
memory: 1024M
reservations:
memory: 256M
# 一次性建表任务,跑完即退出
tdengine-init:
deploy:
resources:
limits:
memory: 128M
# 单节点 consul 常驻约 110~120 MB,给足余量避免贴边
consul:
deploy:
resources:
limits:
memory: 256M
reservations:
memory: 96M
# ---------- 业务服务 ----------
admin-svr:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 128M
admin-ui:
deploy:
resources:
limits:
memory: 64M
openapi:
deploy:
resources:
limits:
memory: 384M
reservations:
memory: 128M
upstream-chan:
deploy:
resources:
limits:
memory: 384M
reservations:
memory: 128M
upstream-proxy:
deploy:
resources:
limits:
memory: 384M
reservations:
memory: 128M
# 全量同步(流量/状态/IMEI)时内存抬升最明显,按文档「500 MB ~ 1 GB」给 1 GB
worker-svr:
deploy:
resources:
limits:
memory: 1024M
reservations:
memory: 256M
app-server:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 128M
app-web:
deploy:
resources:
limits:
memory: 64M
sms:
deploy:
resources:
limits:
memory: 192M
docs:
deploy:
resources:
limits:
memory: 64M
# ---------- profile 可选服务 ----------
bot:
deploy:
resources:
limits:
memory: 192M
onelink-mock:
deploy:
resources:
limits:
memory: 192M
nginx:
deploy:
resources:
limits:
memory: 128M
# 单端口入口(with-gateway);之前没有任何上限
gateway:
deploy:
resources:
limits:
memory: 256M
+7 -1
View File
@@ -1,3 +1,9 @@
# DNS 解析:nginx 只在启动时解析一次 upstream 主机名,admin-svr 重建换 IP 后
# /api/ 会一直 502。127.0.0.11 是 Docker 内置 DNS,resolve + zone 让运行期自动跟随。
resolver 127.0.0.11 valid=10s ipv6=off;
upstream gw_admin_svr { zone gw_admin_svr 64k; server admin-svr:8008 resolve; }
server { server {
listen 80; listen 80;
root /usr/share/nginx/html; root /usr/share/nginx/html;
@@ -40,7 +46,7 @@ server {
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1; proxy_http_version 1.1;
chunked_transfer_encoding off; chunked_transfer_encoding off;
proxy_pass http://admin-svr:8008/api/v1/; proxy_pass http://gw_admin_svr/api/v1/;
proxy_redirect off; proxy_redirect off;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
+72
View File
@@ -0,0 +1,72 @@
# 单端口入口(默认对外 5051)
#
# 用途:把管理端、文档站、OpenAPI 收在一个端口下,便于在外部网关 / DNS
# 只暴露一个地址。编排里由 gateway 服务(profile: with-gateway)加载本文件,
# 容器内监听 80,宿主机发布端口用 GATEWAY_PORT 控制(默认 5051)。
#
# docker compose --profile with-gateway up -d
# GATEWAY_PORT=8088 docker compose --profile with-gateway up -d
#
# 路由:
# / -> 管理端(admin-ui,内部已代理 /api/ 到 admin-svr)
# /docs/ -> 文档站(docs)
# /openapi/ -> 对外 API(openapi)
# /route/open/ -> 运营商能力代理(openapi)
#
# 小程序 / H5 端的静态资源是根路径部署的,不能挂在同一域名的子路径下,
# 需要单独域名或单独端口(见 config/nginx/iotlcm.conf 的 app 段)。
# DNS 解析:nginx 默认只在启动时把 upstream 主机名解析一次,容器重建/重启换了 IP
# 就一直往旧地址打(2026-11-03 踩到:整站 /openapi 全 502,重启网关才恢复)。
# 127.0.0.11 是 Docker 内置 DNS;下面的 resolve + zone 让它在运行期自动跟随。
resolver 127.0.0.11 valid=10s ipv6=off;
upstream gw_admin_ui { zone gw_admin_ui 64k; server admin-ui:80 resolve; }
upstream gw_openapi { zone gw_openapi 64k; server openapi:8000 resolve; }
upstream gw_docs { zone gw_docs 64k; server docs:80 resolve; }
server {
listen 80;
charset utf-8;
client_max_body_size 200m;
access_log /var/log/nginx/access_gateway.log;
error_log /var/log/nginx/error_gateway.log;
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 = /docs {
return 301 /docs/;
}
location ^~ /docs/ {
proxy_pass http://gw_docs;
}
# ---------- 对外 API:版本化接口 ----------
location ^~ /openapi/ {
proxy_pass http://gw_openapi;
}
# ---------- 对外 API:运营商能力代理(白名单 / 共享 Token)----------
location ^~ /route/open/ {
proxy_pass http://gw_openapi;
}
# ---------- 管理端(含 /api/ 与 /upload/,由 admin-ui 自行转发)----------
location / {
proxy_pass http://gw_admin_ui;
}
gzip on;
gzip_min_length 1k;
gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml;
}
+20 -6
View File
@@ -8,28 +8,42 @@
# 把 your-domain.example.com 替换为你的实际域名。 # 把 your-domain.example.com 替换为你的实际域名。
# ============================================================ # ============================================================
# ---------- 后端地址解析 ----------
# 这些 upstream 写的是 compose 服务名。nginx 默认只在**启动时**解析一次,
# 容器重建后 IP 变了就会持续 502(需重启 nginx)。resolve + zone 让它在运行期自动跟随。
# 注意:127.0.0.11 是 Docker 内置 DNS,仅在容器内有效:
# ・在容器内用本文件 → 保持下面的 resolver 不动
# ・把本文件并入宿主机 nginx → 把 resolver 换成宿主机 DNS(或删掉 resolver 与各 upstream 的 resolve 参数)
resolver 127.0.0.11 valid=10s ipv6=off;
upstream iotlcm_admin_api { upstream iotlcm_admin_api {
server admin-svr:8008; zone iotlcm_admin_api 64k;
server admin-svr:8008 resolve;
} }
upstream iotlcm_admin_ui { upstream iotlcm_admin_ui {
server admin-ui:80; zone iotlcm_admin_ui 64k;
server admin-ui:80 resolve;
} }
upstream iotlcm_openapi_api { upstream iotlcm_openapi_api {
server openapi:8000; zone iotlcm_openapi_api 64k;
server openapi:8000 resolve;
} }
upstream iotlcm_docs { upstream iotlcm_docs {
server docs:80; zone iotlcm_docs 64k;
server docs:80 resolve;
} }
upstream iotlcm_app_web { upstream iotlcm_app_web {
server app-web:80; zone iotlcm_app_web 64k;
server app-web:80 resolve;
} }
upstream iotlcm_app_server { upstream iotlcm_app_server {
server app-server:8010; zone iotlcm_app_server 64k;
server app-server:8010 resolve;
} }
# ---------- OpenAPI 对外域名 ---------- # ---------- OpenAPI 对外域名 ----------
+62 -6
View File
@@ -175,6 +175,14 @@ services:
condition: service_healthy condition: service_healthy
rabbitmq: rabbitmq:
condition: service_healthy condition: service_healthy
# 就绪探针:nginx 类容器只在启动时解析一次后端 IP,
# 没有它就无法用 depends_on 的 service_healthy 保证启动顺序(见 gateway)
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:8008/api.json"]
interval: 10s
timeout: 3s
retries: 5
start_period: 20s
admin-ui: admin-ui:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4' image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4'
@@ -191,7 +199,14 @@ services:
networks: networks:
- iotlcm-network - iotlcm-network
depends_on: depends_on:
- admin-svr admin-svr:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1/"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
openapi: openapi:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4' image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
@@ -213,6 +228,12 @@ services:
condition: service_healthy condition: service_healthy
rabbitmq: rabbitmq:
condition: service_healthy condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:8000/api.json"]
interval: 10s
timeout: 3s
retries: 5
start_period: 20s
upstream-chan: upstream-chan:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4' image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4'
@@ -370,6 +391,12 @@ services:
- "127.0.0.1:8019:80" - "127.0.0.1:8019:80"
networks: networks:
- iotlcm-network - iotlcm-network
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1/docs/"]
interval: 10s
timeout: 3s
retries: 5
start_period: 10s
# ---------------- 可选:运营商接口挡板 ---------------- # ---------------- 可选:运营商接口挡板 ----------------
# 模拟中国移动 OneLink 接口,用于在没有真实运营商环境时联调整条链路: # 模拟中国移动 OneLink 接口,用于在没有真实运营商环境时联调整条链路:
@@ -410,7 +437,7 @@ services:
container_name: iotlcm-nginx container_name: iotlcm-nginx
restart: always restart: always
ports: ports:
- "80:80" - "${NGINX_HTTP_PORT:-80}:80"
volumes: volumes:
- ./config/nginx:/etc/nginx/conf.d:ro - ./config/nginx:/etc/nginx/conf.d:ro
- ./data/logs/nginx:/var/log/nginx - ./data/logs/nginx:/var/log/nginx
@@ -418,13 +445,42 @@ services:
networks: networks:
- iotlcm-network - iotlcm-network
depends_on: depends_on:
- admin-ui admin-ui:
- openapi condition: service_healthy
- app-web openapi:
- docs condition: service_healthy
docs:
condition: service_healthy
app-web:
condition: service_started
profiles: profiles:
- with-nginx - with-nginx
# 单端口入口:管理端 + 文档站 + OpenAPI 收在一个端口(默认 5051)对外
gateway:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:nginx-1.30'
container_name: iotlcm-gateway
restart: always
ports:
- "${GATEWAY_PORT:-5051}:80"
volumes:
- ./config/gateway:/etc/nginx/conf.d:ro
- ./data/logs/gateway:/var/log/nginx
networks:
- iotlcm-network
# 后端必须"健康"后才起网关:否则 nginx 会在后端拿到 IP 之前把 upstream
# 解析结果缓存下来,容器重启换 IP 后所有请求变成 502(2026-11-03 踩过)。
# 配置里另有 resolver + resolve,可在运行期自动跟随 IP 变化。
depends_on:
admin-ui:
condition: service_healthy
openapi:
condition: service_healthy
docs:
condition: service_healthy
profiles:
- with-gateway
networks: networks:
iotlcm-network: iotlcm-network:
driver: bridge driver: bridge
+5 -1
View File
@@ -5000,6 +5000,10 @@ INSERT INTO `sys_dict_data` (`dict_type`,`dict_value`,`dict_label`,`dict_sort`,`
-- 注意 is_normal:0=正常、1=异常(见 chan 侧 model.SimChannelConfigItemCache.CanCreateInstance, -- 注意 is_normal:0=正常、1=异常(见 chan 侧 model.SimChannelConfigItemCache.CanCreateInstance,
-- 只有 *IsNormal == 0 才会创建通道实例)。写成 1 会导致「通道未实例化」。 -- 只有 *IsNormal == 0 才会创建通道实例)。写成 1 会导致「通道未实例化」。
-- 这里的取值与内网演示环境种子不同(那边是 1),属开源版修正。 -- 这里的取值与内网演示环境种子不同(那边是 1),属开源版修正。
--
-- app_key / app_secret 是挡板(onelink-mock)的凭据,chan 侧用它们向挡板换 Token
-- (appid=app_key,password=app_secret)。取值必须与挡板默认凭据一致,
-- 见 onelink-mock/internal/service/store.go 的 defaultStore()。
-- ============================================================ -- ============================================================
INSERT INTO `t_sim_channels` ( INSERT INTO `t_sim_channels` (
@@ -5012,7 +5016,7 @@ INSERT INTO `t_sim_channels` (
`card_info_count`,`stock_card_count`,`cancellation_card_count` `card_info_count`,`stock_card_count`,`cancellation_card_count`
) VALUES ( ) VALUES (
34,'sim_yd_mock_v5','channel_yd_mock','移动模拟通道','{}',0,0, 34,'sim_yd_mock_v5','channel_yd_mock','移动模拟通道','{}',0,0,
'中国移动00',NULL,'100059999270000006','password123',NULL, '中国移动00',NULL,'demo_appid','demo_password',NULL,
NULL,NULL,'', NULL,NULL,'',
NULL,NULL,'http://onelink-mock:8080/',NULL,'000000', NULL,NULL,'http://onelink-mock:8080/',NULL,'000000',
1,1,'2026-10-19 15:35:03',NULL,NOW(), 1,1,'2026-10-19 15:35:03',NULL,NOW(),
+7 -2
View File
@@ -139,8 +139,8 @@ docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
| admin-svr | `iotlcm-admin-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4` | | admin-svr | `iotlcm-admin-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4` |
| admin-ui | `iotlcm-admin-ui-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4` | | admin-ui | `iotlcm-admin-ui-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4` |
| openapi | `iotlcm-openapi-<TAG>` | `.../iotlcm/iotlcm:iotlcm-openapi-v1.0.4` | | openapi | `iotlcm-openapi-<TAG>` | `.../iotlcm/iotlcm:iotlcm-openapi-v1.0.4` |
| app-server | `iotlcm-app-server-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-server-v1.0.4` | | app-server | `iotlcm-app-server-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2` |
| app-web | `iotlcm-app-web-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-web-v1.0.4` | | app-web | `iotlcm-app-web-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-web-v1.0.2` |
| upstream-chan | `iotlcm-upstream-chan-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4` | | upstream-chan | `iotlcm-upstream-chan-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4` |
| upstream-proxy | `iotlcm-upstream-proxy-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4` | | upstream-proxy | `iotlcm-upstream-proxy-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4` |
| worker-svr | `iotlcm-worker-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4` | | worker-svr | `iotlcm-worker-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4` |
@@ -151,6 +151,11 @@ docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,从上游同步到同一仓库即可,tag 为 `<组件>-<版本>`,见下节。 基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,从上游同步到同一仓库即可,tag 为 `<组件>-<版本>`,见下节。
::: tip app 线是独立版本
`iotlcm-app-server` 与 `iotlcm-app-web` 属于**小程序线**,版本号与平台 tag 解耦:平台发 v1.0.4 时它们仍是 `v1.0.2`。
自建镜像时按 `iotlcm-app-<x>-v1.0.2` 命名,不要跟着平台版本走。
:::
::: warning tag 必须带上服务名 ::: warning tag 必须带上服务名
同一个仓库里 tag 不能重复。只用版本号(如 `v1.0.4`)会让 12 个服务互相覆盖,因此 tag 固定为 `<服务名>-<版本>`。 同一个仓库里 tag 不能重复。只用版本号(如 `v1.0.4`)会让 12 个服务互相覆盖,因此 tag 固定为 `<服务名>-<版本>`。
::: :::
+11 -6
View File
@@ -16,18 +16,23 @@ cd iotlcm/deploy
```text ```text
deploy/ deploy/
├── docker-compose.yml # 服务编排(含默认口令与端口) ├── docker-compose.yml # 服务编排(含默认口令与端口)
├── config/ # 各服务配置,10 份,已带可用默认值 ├── config/ # 各服务配置 + gateway / nginx 两种入口配置
└── schema/ # 数据库初始化脚本,13 个,首次启动自动执行 ├── schema/init.sql # MySQL 初始化(单文件,95 表 + 种子数据)
├── tdengine-init/ # 时序库建库建表(首次启动自动执行)
└── data/ # 运行数据,首次启动自动创建
``` ```
## 部署内容 ## 部署内容
| 类别 | 服务 | | 类别 | 服务 |
| --- | --- | | --- | --- |
| 基础组件 | MySQL、Redis、RabbitMQ、Consul | | 基础组件 | MySQL、Redis、RabbitMQ、Consul、TDengine(流量历史) |
| 管理端 | admin-svr(后端)、admin-ui(前端) | | 管理端 | admin-svr(后端 8008)、admin-ui(前端 8009) |
| 对外接口 | openapi | | 对外接口 | openapi(8000) |
| 小程序 / H5 | app-server(后端)、app-web(前端) | | 小程序 / H5 | app-server(8010)、app-web(8018) |
| 通道 | upstream-chan(8110 / 8310)、upstream-proxy(8510) |
| 异步与配套 | worker-svr、sms(11190)、docs(8019) |
| 可选 | onelink-mock(接口挡板)、bot(飞书通知)、nginx(多域名入口)、gateway(单端口入口 5051) |
| 运营商对接 | upstream-chan(协议适配)、upstream-proxy(缓存转发) | | 运营商对接 | upstream-chan(协议适配)、upstream-proxy(缓存转发) |
| 异步任务 | worker-svr(批次执行、全量同步) | | 异步任务 | worker-svr(批次执行、全量同步) |
| 配套 | sms(短信)、docs(本文档站) | | 配套 | sms(短信)、docs(本文档站) |
+1 -1
View File
@@ -137,7 +137,7 @@ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
### 7. 浏览器登录 ### 7. 浏览器登录
访问 `http://<宿主机IP>:8009`: 访问 `http://127.0.0.1:8009`(编排默认把业务端口绑定在 `127.0.0.1`;需要从其他机器访问时,用 `--profile with-nginx` 起反向代理,或改绑定地址):
| 项 | 值 | | 项 | 值 |
| --- | --- | | --- | --- |
+31 -1
View File
@@ -88,6 +88,36 @@ docker compose --profile with-bot up -d
bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/full-sync`),该目录有数据后才会产生通知。 bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/full-sync`),该目录有数据后才会产生通知。
## 单端口入口
### 用途
只对外暴露**一个端口**(默认 5051),把管理端、文档站、OpenAPI 收在一起。适合外面还有一层网关 / DNS,
或者只想开放一个端口的环境。
### 路由
| 路径 | 去向 |
| --- | --- |
| `/` | 管理端(admin-ui,内部已代理 `/api/` 到 admin-svr) |
| `/docs/` | 文档站 |
| `/openapi/` | 对外 API |
| `/route/open/` | 运营商能力代理(白名单 / 共享 Token) |
### 启动
```bash
docker compose --profile with-gateway up -d
# 换端口
GATEWAY_PORT=8088 docker compose --profile with-gateway up -d
```
配置见 `config/gateway/nginx.conf`。
::: tip 小程序 / H5 端不在单端口内
`app-web` 的静态资源是根路径部署的,挂到子路径会白屏。小程序端需要单独域名或端口,用下面的反向代理方式。
:::
## 反向代理 ## 反向代理
### 用途 ### 用途
@@ -110,7 +140,7 @@ bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/fu
docker compose --profile with-nginx up -d docker compose --profile with-nginx up -d
``` ```
默认监听 80,改 `docker-compose.yml` 的 `NGINX_HTTP_PORT` 可调整。 默认监听 80,对外端口由 `NGINX_HTTP_PORT` 控制:`NGINX_HTTP_PORT=8080 docker compose --profile with-nginx up -d`。
### 使用宿主机 nginx ### 使用宿主机 nginx