chore: 并入 iotlcm-docs 历史到 docs/(原提交 947e22e)
@@ -0,0 +1,17 @@
|
|||||||
|
node_modules
|
||||||
|
.vitepress/cache
|
||||||
|
.vitepress/dist
|
||||||
|
.vitepress/.temp
|
||||||
|
dist
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.war
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.playwright-mcp/
|
||||||
|
.kilo/
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
Dockerfile
|
||||||
|
README.md
|
||||||
|
npm-debug.log*
|
||||||
@@ -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,80 @@
|
|||||||
|
name: trigger-jenkins-oss
|
||||||
|
|
||||||
|
# 打 tag 时触发 Jenkins 构建开源镜像并推送到镜像仓库。
|
||||||
|
# 仅在 tag 推送时触发;main 分支的日常提交不触发。
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# 同一 ref 连续触发时取消旧任务,只跑最新的
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
trigger:
|
||||||
|
runs-on: host
|
||||||
|
steps:
|
||||||
|
- name: 触发 Jenkins 镜像构建
|
||||||
|
env:
|
||||||
|
JU: ${{ secrets.JENKINS_USER }}
|
||||||
|
JT: ${{ secrets.JENKINS_API_TOKEN }}
|
||||||
|
JURL: ${{ secrets.JENKINS_URL }}
|
||||||
|
JOB_PATH: ${{ secrets.JENKINS_JOB_PATH }}
|
||||||
|
REPO_NAME: ${{ github.event.repository.name }}
|
||||||
|
TAG_NAME: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# 只有 tag 推送才构建;手动触发时按 tag 取,没有就退出
|
||||||
|
case "${GITHUB_REF:-}" in
|
||||||
|
refs/tags/v*) ;;
|
||||||
|
*)
|
||||||
|
if [ "${GITHUB_EVENT_NAME:-}" != "workflow_dispatch" ]; then
|
||||||
|
echo "非 tag 推送,跳过:${GITHUB_REF:-unknown}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${TAG_NAME:-}" ]; then
|
||||||
|
echo "未取到 tag 名,退出"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# job 在 Jenkins 文件夹下的路径(组织密钥 JENKINS_JOB_PATH),
|
||||||
|
# 例如 job/iotlcm-opensrc/job/iotlcm-oss-build。
|
||||||
|
# 用密钥而非硬编码,Jenkins 里移动 job 时不用改 11 个仓。
|
||||||
|
JP="${JOB_PATH:-job/iotlcm-oss-build}"
|
||||||
|
|
||||||
|
echo "仓库:$REPO_NAME 版本:$TAG_NAME job:$JP"
|
||||||
|
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
# 取 CSRF crumb
|
||||||
|
curl -s -c "$WORK/cj" -u "$JU:$JT" "$JURL/crumbIssuer/api/json" -o "$WORK/crumb.json"
|
||||||
|
CR=$(python3 -c "import json;print(json.load(open('$WORK/crumb.json'))['crumb'])")
|
||||||
|
|
||||||
|
# 触发参数化任务:SERVICES=仓库名,VERSION=tag
|
||||||
|
code=$(curl -s -o "$WORK/out" -w '%{http_code}' -X POST -b "$WORK/cj" -u "$JU:$JT" \
|
||||||
|
-H "Jenkins-Crumb: $CR" \
|
||||||
|
--data-urlencode "SERVICES=$REPO_NAME" \
|
||||||
|
--data-urlencode "VERSION=$TAG_NAME" \
|
||||||
|
--data-urlencode "FORCE=true" \
|
||||||
|
"$JURL/$JP/buildWithParameters")
|
||||||
|
|
||||||
|
echo "Jenkins 触发 HTTP $code"
|
||||||
|
if [ "$code" != "201" ] && [ "$code" != "200" ] && [ "$code" != "302" ] && [ "$code" != "303" ]; then
|
||||||
|
echo "触发失败:"
|
||||||
|
cat "$WORK/out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
LINK="$JURL/$JP/"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Jenkins 构建:$LINK"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "::notice title=Jenkins 镜像构建::[$LINK]($LINK)"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
node_modules
|
||||||
|
.vitepress/cache
|
||||||
|
.vitepress/dist
|
||||||
|
.vitepress/.temp
|
||||||
|
dist
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.war
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.playwright-mcp/
|
||||||
|
.kilo/
|
||||||
|
.playwright-cli/
|
||||||
|
.apitest-output/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
registry=https://registry.npmmirror.com/
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
|
||||||
|
const webManualItems = [
|
||||||
|
{ text: '快速开始', link: '/manual/web/' },
|
||||||
|
{ text: '首页与通用操作', link: '/manual/web/home' },
|
||||||
|
{ text: '卡信息', link: '/manual/web/card-info' },
|
||||||
|
{ text: '智能诊断', link: '/manual/web/intelligent-diagnosis' },
|
||||||
|
{ text: '业务受理与达量断网', link: '/manual/web/business-operations' },
|
||||||
|
{ text: '套餐配置与定价', link: '/manual/web/packages-and-pricing' },
|
||||||
|
{ text: '预警管理', link: '/manual/web/warnings' },
|
||||||
|
{ text: '入库、库存与出库', link: '/manual/web/inventory' },
|
||||||
|
{ text: '通道与接入管理', link: '/manual/web/channels' },
|
||||||
|
{ text: '企业、用户与角色', link: '/manual/web/enterprise-management' },
|
||||||
|
{ text: '工单管理', link: '/manual/web/work-orders' },
|
||||||
|
{ text: 'API 凭证与执行日志', link: '/manual/web/api-and-logs' },
|
||||||
|
{ text: '常见问题', link: '/manual/web/faq' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const miniProgramManualItems = [
|
||||||
|
{ text: '快速开始', link: '/manual/mini-program/' },
|
||||||
|
{ text: '首页公开查卡', link: '/manual/mini-program/home' },
|
||||||
|
{ text: '公开卡片详情', link: '/manual/mini-program/devices' },
|
||||||
|
{ text: '卡片排查', link: '/manual/mini-program/diagnosis' },
|
||||||
|
{ text: '登录后办理概览', link: '/manual/mini-program/business' },
|
||||||
|
{ text: '客户管理与卡片划拨', link: '/manual/mini-program/customers-and-allocation' },
|
||||||
|
{ text: '工单服务', link: '/manual/mini-program/work-orders' },
|
||||||
|
{ text: '批次执行日志', link: '/manual/mini-program/execution-logs' },
|
||||||
|
{ text: '消息与个人中心', link: '/manual/mini-program/messages-and-profile' },
|
||||||
|
{ text: '常见问题', link: '/manual/mini-program/faq' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function manualSidebar(_active: 'web' | 'mini' | 'both' = 'both') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: 'Web端',
|
||||||
|
collapsed: false,
|
||||||
|
items: webManualItems,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '小程序端',
|
||||||
|
collapsed: false,
|
||||||
|
items: miniProgramManualItems,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
srcDir: './docs',
|
||||||
|
base: '/docs/',
|
||||||
|
title: 'iotLCM',
|
||||||
|
description: 'iotLCM documentation center',
|
||||||
|
lang: 'zh-CN',
|
||||||
|
cleanUrls: true,
|
||||||
|
lastUpdated: false,
|
||||||
|
head: [
|
||||||
|
['link', { rel: 'icon', type: 'image/png', href: '/docs/favicon.png' }]
|
||||||
|
],
|
||||||
|
vite: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: 'serve-root-favicon-in-dev',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use('/favicon.ico', (_request, response) => {
|
||||||
|
response.writeHead(302, { Location: '/docs/favicon.png' })
|
||||||
|
response.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
themeConfig: {
|
||||||
|
logo: undefined,
|
||||||
|
nav: [
|
||||||
|
{ text: '文档首页', link: '/' },
|
||||||
|
{ text: '使用手册', link: '/manual/' },
|
||||||
|
{ text: 'OpenAPI', link: '/openapi/' },
|
||||||
|
{ text: '共享 Token 接口', link: '/openapi/operator/shared-token/' },
|
||||||
|
{ text: '部署手册', link: '/deploy/' }
|
||||||
|
],
|
||||||
|
sidebar: {
|
||||||
|
'/deploy/': [
|
||||||
|
{
|
||||||
|
text: '部署手册',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '部署概览', link: '/deploy/' },
|
||||||
|
{ text: '环境准备', link: '/deploy/prerequisites' },
|
||||||
|
{ text: '配置说明', link: '/deploy/configuration' },
|
||||||
|
{ text: '启动与验证', link: '/deploy/install' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '其他部署方式',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '方式对比与选型', link: '/deploy/alternatives' },
|
||||||
|
{ text: '二进制部署', link: '/deploy/binary' },
|
||||||
|
{ text: '自建镜像', link: '/deploy/build-image' },
|
||||||
|
{ text: '分离部署', link: '/deploy/split' },
|
||||||
|
{ text: '离线部署', link: '/deploy/offline' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '运维',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '可选服务', link: '/deploy/optional' },
|
||||||
|
{ text: '升级与备份', link: '/deploy/operations' },
|
||||||
|
{ text: '故障排查', link: '/deploy/troubleshooting' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'/manual/': manualSidebar(),
|
||||||
|
'/manual/web/': manualSidebar('web'),
|
||||||
|
'/manual/mini-program/': manualSidebar('mini'),
|
||||||
|
'/openapi/operator/': [
|
||||||
|
{
|
||||||
|
text: '共享 Token 接口',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '概览', link: '/openapi/operator/' },
|
||||||
|
{ text: '共享 Token 接口', link: '/openapi/operator/shared-token/' },
|
||||||
|
{ text: '白名单转发', link: '/openapi/operator/mobile-v5/' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'/openapi/': [
|
||||||
|
{
|
||||||
|
text: 'V3 接口',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '概览与鉴权', link: '/openapi/v3/' },
|
||||||
|
{ text: '获取调用凭证', link: '/openapi/v3/token' },
|
||||||
|
{ text: '查询类', link: '/openapi/v3/query' },
|
||||||
|
{ text: '业务类', link: '/openapi/v3/common' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '旧版兼容接口',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: '概览与鉴权', link: '/openapi/legacy/' },
|
||||||
|
{ text: '查询类', link: '/openapi/legacy/query' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
level: [2, 3],
|
||||||
|
label: '本页目录'
|
||||||
|
},
|
||||||
|
docFooter: {
|
||||||
|
prev: '上一页',
|
||||||
|
next: '下一页'
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
provider: 'local'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
message: 'iotLCM 文档中心',
|
||||||
|
copyright: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/* 截图点击放大(medium-zoom)—— 全站生效,范围由 .vitepress/theme/index.ts 的 ZOOM_SELECTOR 定义 */
|
||||||
|
|
||||||
|
/* 叠加层必须盖住导航和侧边栏:默认主题 --vp-z-index-nav 为 30、
|
||||||
|
--vp-z-index-sidebar 为 60,所以取 100。 */
|
||||||
|
.medium-zoom-overlay {
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium-zoom-image--opened {
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JS 尚未挂载 medium-zoom 时也提前给出「可点击」的鼠标提示 */
|
||||||
|
.vp-doc img:not(a img):not(.no-zoom) {
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 单张图片保持原始尺寸,并在正文区域水平居中。 */
|
||||||
|
.vp-doc img {
|
||||||
|
display: block;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 单行只有一张图片时缩放到内容区域的 40%。 */
|
||||||
|
.vp-doc p > img:only-child,
|
||||||
|
.vp-doc li > img:only-child {
|
||||||
|
width: 40%;
|
||||||
|
max-width: 40%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Web 端手册截图恢复为内容区域全宽展示。 */
|
||||||
|
.vp-doc img[src*="/images/manual/"]:not([src*="mini-program"]) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 同一行包含两张图片时,整行缩放到内容区域的 40%。 */
|
||||||
|
.vp-doc p:has(> img:nth-child(2):last-child) {
|
||||||
|
display: flex;
|
||||||
|
width: 80%;
|
||||||
|
max-width: 80%;
|
||||||
|
height: auto;
|
||||||
|
line-height: 0;
|
||||||
|
margin: 32px auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc p:has(> img:nth-child(2):last-child) > img {
|
||||||
|
width: calc(50% - 4px);
|
||||||
|
max-width: calc(50% - 4px);
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markdown 内嵌的双图网格统一缩放到内容区域的 80%。 */
|
||||||
|
.vp-doc div[style*="repeat(2"] {
|
||||||
|
width: 80% !important;
|
||||||
|
max-width: 80% !important;
|
||||||
|
margin: 32px auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[style*="repeat(2"] img {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[style*="repeat(3"] img {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[style*="repeat(2"] figcaption,
|
||||||
|
.vp-doc div[style*="repeat(3"] figcaption {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc[class*="_docs_openapi_"] table {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openapi-page .custom-block.warning,
|
||||||
|
.openapi-page .custom-block.danger {
|
||||||
|
background-color: var(--vp-code-block-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.openapi-page .custom-block.warning code,
|
||||||
|
.openapi-page .custom-block.danger code {
|
||||||
|
background-color: var(--vp-code-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.vp-doc[class*="_docs_openapi_"] table {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin: 28px 0 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-card {
|
||||||
|
display: flex;
|
||||||
|
min-height: 190px;
|
||||||
|
padding: 24px;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-card:hover {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
box-shadow: 0 8px 24px rgb(60 90 180 / 12%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-title {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-desc {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-entry-link {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 18px;
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-program-entry {
|
||||||
|
display: flex;
|
||||||
|
width: min(100%, 520px);
|
||||||
|
margin: 28px auto 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .mini-program-entry + h2 {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-program-entry__qr {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-program-entry__qr img {
|
||||||
|
width: 196px;
|
||||||
|
max-width: 58vw;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-program-entry__qr figcaption {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-program-entry__hint {
|
||||||
|
margin: 14px 0 0;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.manual-entry-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
import mediumZoom from 'medium-zoom'
|
||||||
|
import { onContentUpdated } from 'vitepress'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import type { Theme } from 'vitepress'
|
||||||
|
|
||||||
|
import './custom.css'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参与「点击放大」的图片范围。
|
||||||
|
*
|
||||||
|
* `.vp-doc` 是 VitePress 默认主题给正文容器加的类名,使用手册 9 页与接口文档
|
||||||
|
* 全部页面共用这一套布局,所以在这里定义一次即对全站生效。
|
||||||
|
*
|
||||||
|
* 排除项:
|
||||||
|
* a img 图片本身是链接(如带外链的截图),点击应跳转而不是放大
|
||||||
|
* .no-zoom 需要单独关闭放大的图片,示例:
|
||||||
|
* `<img src="/images/manual/xx.png" class="no-zoom" alt="xx">`
|
||||||
|
*/
|
||||||
|
const ZOOM_SELECTOR = '.vp-doc img:not(a img):not(.no-zoom)'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全站只保留一个 medium-zoom 实例,但【必须惰性创建】。
|
||||||
|
*
|
||||||
|
* 两个坑叠在一起,才得到这个写法:
|
||||||
|
*
|
||||||
|
* 1. 不能在模块顶层创建实例。medium-zoom 1.1.0 的工厂函数第一件事就是读
|
||||||
|
* `window.Promise`(dist/medium-zoom.min.js 里即 `var Promise = window.Promise || ...`),
|
||||||
|
* 而主题模块在 `vitepress build` 时会被 Node 端 SSR 打包并执行 —— 没有 window,
|
||||||
|
* 直接 `ReferenceError: window is not defined` 构建失败。
|
||||||
|
* 所以实例只能在浏览器生命周期回调里创建。
|
||||||
|
*
|
||||||
|
* 2. 也不能每次路由切换都 `mediumZoom(...)` 新建。每个实例都会在 document 上注册
|
||||||
|
* click / keyup / scroll、在 window 上注册 resize 监听器,且没有卸载入口
|
||||||
|
* (见 dist/medium-zoom.esm.js:576-581)。翻 N 个页面就会留下 N 组永不释放的
|
||||||
|
* 全局监听器。正确做法是复用实例,路由切换时只做 attach —— attach 内部会跳过
|
||||||
|
* 已挂载的图片(dist/medium-zoom.esm.js:199-204),因此重复调用是幂等的。
|
||||||
|
*/
|
||||||
|
let zoom: ReturnType<typeof mediumZoom> | null = null
|
||||||
|
|
||||||
|
const attachZoom = () => {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
if (!zoom) {
|
||||||
|
zoom = mediumZoom({
|
||||||
|
background: 'var(--vp-c-bg)',
|
||||||
|
margin: 24,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
zoom.attach(ZOOM_SELECTOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeZoomOnClick = (event: MouseEvent) => {
|
||||||
|
const target = event.target as Element | null
|
||||||
|
if (!zoom || !target) return
|
||||||
|
if (target.closest('.medium-zoom-image--opened, .medium-zoom-overlay')) {
|
||||||
|
void zoom.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const theme: Theme = {
|
||||||
|
extends: DefaultTheme,
|
||||||
|
setup() {
|
||||||
|
// onContentUpdated 由 VitePress 在「新页面内容挂载完成后」触发,覆盖首次进入
|
||||||
|
// 与 SPA 客户端路由切换。它挂在路由组件的 onVnodeMounted 上
|
||||||
|
// (见 vitepress/dist/client/app/components/Content.js:13-20),
|
||||||
|
// 比 watch(route.path) + nextTick 更可靠 —— 后者可能在异步页面组件挂载前就执行,
|
||||||
|
// 导致某些页面图片没挂上监听、点击放大静默失效。
|
||||||
|
onContentUpdated(attachZoom)
|
||||||
|
|
||||||
|
// 兜底:主题 setup 时内容已就绪(不经过 onVnodeMounted 的极端情况)也能挂上
|
||||||
|
onMounted(attachZoom)
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
document.addEventListener('click', closeZoomOnClick, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default theme
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
||||||
|
&& apk add --no-cache git
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json .npmrc ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm run docs:build
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
COPY --from=builder /app/.vitepress/dist /usr/share/nginx/html
|
||||||
|
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
@@ -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,89 @@
|
|||||||
|
# iotlcm-docs
|
||||||
|
|
||||||
|
iotLCM 物联网卡全生命周期管理平台的**使用手册与 OpenAPI 合同**。
|
||||||
|
|
||||||
|
- **使用手册**:Web 管理端(企业客户视角)与小程序端的操作说明,含截图
|
||||||
|
- **OpenAPI 合同**:V3 接口、旧版兼容接口、运营商能力代理(白名单转发 / 共享 Token)的鉴权方式、请求参数与响应结构
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
VitePress 1.6 + Vue 3,产物为纯静态站点。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
iotlcm-docs/
|
||||||
|
├── docs/ # 文档源(VitePress srcDir)
|
||||||
|
│ ├── index.md # 文档首页
|
||||||
|
│ ├── manual/ # 使用手册
|
||||||
|
│ │ ├── web/ # Web 管理端
|
||||||
|
│ │ └── mini-program/ # 小程序端
|
||||||
|
│ ├── openapi/ # OpenAPI 合同
|
||||||
|
│ │ ├── v3/ # V3 接口
|
||||||
|
│ │ ├── legacy/ # 旧版兼容接口
|
||||||
|
│ │ └── operator/ # 运营商能力代理
|
||||||
|
│ └── public/ # 静态资源(favicon、截图)
|
||||||
|
├── .vitepress/ # VitePress 配置与主题定制
|
||||||
|
├── docker/ # nginx 配置
|
||||||
|
├── Dockerfile
|
||||||
|
└── docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run dev # http://127.0.0.1:5173/docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
> 入口地址带 `/docs/`:配置里 `base: '/docs/'`,直接访问根路径会 404。
|
||||||
|
|
||||||
|
构建与预览:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # 产物在 .vitepress/dist
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
站点部署在子路径 `/docs/`(见 `.vitepress/config.mts` 的 `base`)。本地开发时根路径会自动重定向 favicon。
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
**完整部署手册见文档站:[部署手册](https://git.5iot.com/iotlcm-public/iotlcm-docs) → `docs/deploy/`**,包含推荐环境、编译方法、nginx / 对象存储 / 离线环境部署方式与常见问题。
|
||||||
|
|
||||||
|
最短路径(容器,推荐):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t iotlcm-docs:$(git rev-parse --short HEAD) .
|
||||||
|
docker run -d --name iotlcm-docs -p 8019:80 iotlcm-docs:latest
|
||||||
|
# 访问 http://127.0.0.1:8019/docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
多阶段构建:Node 阶段执行 `vitepress build`,最终镜像只含 nginx 与静态产物。
|
||||||
|
|
||||||
|
> ⚠️ **路径前缀**:`base: '/docs/'` 使资源引用为绝对路径 `/docs/assets/*`,而 dist 是平铺的。`docker/nginx.conf` 已用 `alias` 剥掉前缀,**不要**改成从根路径直接服务产物,否则白屏。
|
||||||
|
|
||||||
|
`docker-compose.yml` 里的镜像名需替换成你自己的镜像仓库地址;对外署时请用不可变标签(commit sha)而不是 `latest`。
|
||||||
|
|
||||||
|
## 文档里的占位符
|
||||||
|
|
||||||
|
文档中的服务地址、凭据等均为占位示例:
|
||||||
|
|
||||||
|
| 占位 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `iotlcm.example.com` | Web 管理端地址 |
|
||||||
|
| `api.iotlcm.example.com` | OpenAPI 服务地址 |
|
||||||
|
| `YOUR_APP_ID` / `YOUR_APP_SECRET` | 平台分配的应用凭据 |
|
||||||
|
| `YOUR_ACCESS_TOKEN` / `YOUR_TRANSID` | 调用方凭据与流水号 |
|
||||||
|
| `141****5152` | 示例手机号 |
|
||||||
|
|
||||||
|
替换成你自己的环境即可。
|
||||||
|
|
||||||
|
## 相关项目
|
||||||
|
|
||||||
|
- [iotlcm-openapi](https://git.5iot.com/iotlcm-public/iotlcm-openapi) —— OpenAPI 服务实现
|
||||||
|
- [iotlcm-admin-svr](https://git.5iot.com/iotlcm-public/iotlcm-admin-svr) —— Web 管理端后端
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Apache License 2.0](./LICENSE)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
docs:
|
||||||
|
image: iotlcm-docs:latest
|
||||||
|
container_name: iotlcm-docs
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- iotlcm-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
iotlcm-network:
|
||||||
|
external: true
|
||||||
|
name: iotlcm-network
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# VitePress 配置 base: '/docs/',构建产物里的资源引用是绝对路径 /docs/assets/xxx,
|
||||||
|
# 而 dist 目录是平铺的(index.html、assets/ 直接在根)。
|
||||||
|
# 因此必须在 /docs/ 路径下提供服务,用 alias 把前缀剥掉再落到产物目录,
|
||||||
|
# 否则页面能返回 200 但 JS/CSS 全部 404 → 白屏。
|
||||||
|
location /docs/ {
|
||||||
|
alias /usr/share/nginx/html/;
|
||||||
|
try_files $uri $uri.html $uri/ $uri/index.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 根路径跳到文档入口,避免访问 / 时白屏
|
||||||
|
location = / {
|
||||||
|
return 302 /docs/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri.html $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
title: 其他部署方式
|
||||||
|
description: 二进制部署、自建镜像、分离部署与离线部署
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 其他部署方式
|
||||||
|
|
||||||
|
[启动与验证](install) 描述的是 **Docker Compose 单机部署**,也是推荐方式。本组文档覆盖其他场景。
|
||||||
|
|
||||||
|
## 方式对比
|
||||||
|
|
||||||
|
| 方式 | 适用场景 | 复杂度 | 参考 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| **单机 Compose**(推荐) | 试用、中小规模生产 | 低 | [启动与验证](install) |
|
||||||
|
| **二进制部署** | 不能装 Docker,或需纳入 systemd 体系 | 中 | [二进制部署](binary) |
|
||||||
|
| **自建镜像** | 需改代码、加依赖、适配私有基础镜像 | 中 | [自建镜像](build-image) |
|
||||||
|
| **分离部署** | 数据库走云托管,或中间件已有独立集群 | 中 | [分离部署](split) |
|
||||||
|
| **离线部署** | 服务器无法访问镜像仓库 | 中 | [离线部署](offline) |
|
||||||
|
|
||||||
|
## 服务依赖关系
|
||||||
|
|
||||||
|
选择部署方式前需了解各服务依赖哪些组件:
|
||||||
|
|
||||||
|
| 服务 | 依赖 |
|
||||||
|
| --- | --- |
|
||||||
|
| admin-svr | MySQL、Redis、RabbitMQ |
|
||||||
|
| openapi | MySQL、Redis、RabbitMQ |
|
||||||
|
| worker-svr | MySQL、Redis、RabbitMQ |
|
||||||
|
| app-server | MySQL、Redis |
|
||||||
|
| upstream-chan | MySQL、Redis、Consul |
|
||||||
|
| upstream-proxy | MySQL、Redis、Consul |
|
||||||
|
| admin-ui | admin-svr(反向代理) |
|
||||||
|
| nginx | admin-ui、openapi、app-web、docs |
|
||||||
|
| sms | 无 |
|
||||||
|
| docs | 无 |
|
||||||
|
| onelink-mock | 无 |
|
||||||
|
|
||||||
|
## 通用前提
|
||||||
|
|
||||||
|
无论哪种方式,这三项不变:
|
||||||
|
|
||||||
|
1. **配置文件必须能被服务读到**——容器部署时挂载到 `/app/config`,二进制部署时放在工作目录的 `config/`
|
||||||
|
2. **`JWT_SIGNING_KEY` 在 admin-svr、openapi、app-server 三处必须一致**
|
||||||
|
3. **数据库需先完成初始化**——`schema/` 脚本按顺序执行一次
|
||||||
|
|
||||||
|
## 服务间通信方式
|
||||||
|
|
||||||
|
配置文件里通过**服务名**互访,例如:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
database:
|
||||||
|
default:
|
||||||
|
link: "mysql:u_iotlcm:...@tcp(mysql:3306)/db_iotlcm"
|
||||||
|
upstream:
|
||||||
|
proxy:
|
||||||
|
address: "upstream-proxy:8510"
|
||||||
|
```
|
||||||
|
|
||||||
|
这些名称在单机 Compose 中由 Docker 网络解析。**切换到二进制部署或分离部署时必须改为实际地址**(`127.0.0.1` 或内网 IP),这是最容易遗漏的一步。
|
||||||
|
|
||||||
|
::: danger 最常见的部署失败原因
|
||||||
|
只改了数据库地址,忘了改 `upstream-proxy:8510`、`upstream-chan:8110` 这类服务名。症状是服务能启动、数据库也通,但批次执行、智能诊断全部超时。
|
||||||
|
:::
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
---
|
||||||
|
title: 二进制部署
|
||||||
|
description: 直接编译 Go 程序运行,不依赖 Docker
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 二进制部署
|
||||||
|
|
||||||
|
不使用容器,把各服务编译为可执行文件,用 systemd 托管。
|
||||||
|
|
||||||
|
## 适用场景
|
||||||
|
|
||||||
|
- 服务器不允许安装 Docker
|
||||||
|
- 需要把服务纳入既有的 systemd / 进程管理体系统一运维
|
||||||
|
- 需要针对特定 CPU 架构或内核参数做优化
|
||||||
|
|
||||||
|
## 构建环境
|
||||||
|
|
||||||
|
| 项 | 要求 |
|
||||||
|
| --- | --- |
|
||||||
|
| Go | 按各服务 `go.mod` 要求,最低 1.23(见下表) |
|
||||||
|
| GCC | admin-svr 需要(`CGO_ENABLED=1`,内存库用 SQLite) |
|
||||||
|
| 系统 | 建议与目标服务器同发行版,避免 libc 差异 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debian / Ubuntu
|
||||||
|
apt-get install -y build-essential
|
||||||
|
|
||||||
|
# CentOS / Rocky
|
||||||
|
yum groupinstall -y "Development Tools"
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 其余服务可静态编译
|
||||||
|
除 admin-svr 外,其余服务 `CGO_ENABLED=0`,产物为静态二进制,可直接拷到任意同架构 Linux 运行。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 各服务构建参数
|
||||||
|
|
||||||
|
| 服务 | 仓库 | Go 版本 | CGO | 构建目录 | 产物名 | 监听端口 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| admin-svr | `iotlcm-admin-svr` | 1.23 | **需开启** | `server/` | `iotlcmadmin_svc` | 8008 |
|
||||||
|
| openapi | `iotlcm-openapi` | 1.23 | 关闭 | 根目录 | `iotlcm-openapi` | 8000 |
|
||||||
|
| worker-svr | `iotlcm-worker-svr` | 1.25 | 关闭 | 根目录 | `iotlcm-worker-svr` | 无(队列消费) |
|
||||||
|
| upstream-chan | `iotlcm-upstream-chan` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamchan_svc` | 8110 / 8310 |
|
||||||
|
| upstream-proxy | `iotlcm-upstream-proxy` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamproxy_svc` | 8510 |
|
||||||
|
| app-server | `iotlcm-app` | 1.23 | 关闭 | `server/` | `iotlcm-app-server` | 8010 |
|
||||||
|
| sms | `iotlcm-sms` | 1.25 | 关闭 | 根目录 | `iotlcm-sms` | 11190 / 11200 |
|
||||||
|
| bot | `iotlcm-bot` | 1.23 | 关闭 | `cmd/iotlcm-bot` | `iotlcm-bot` | 无 |
|
||||||
|
|
||||||
|
::: warning 仓库里的 Dockerfile 不能直接照搬
|
||||||
|
部分仓库的 Dockerfile 基础镜像指向私有仓库(见[自建镜像](build-image)),二进制构建不受影响,但 `FROM` 行需要替换。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 构建
|
||||||
|
|
||||||
|
### admin-svr(需要 CGO)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.5iot.com/iotlcm-public/iotlcm-admin-svr.git
|
||||||
|
cd iotlcm-admin-svr/server
|
||||||
|
|
||||||
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -trimpath \
|
||||||
|
-ldflags "-s -w \
|
||||||
|
-X main.BuildVersion=$(git describe --tags --always) \
|
||||||
|
-X main.CommitID=$(git rev-parse --short HEAD) \
|
||||||
|
-X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||||
|
-o iotlcmadmin_svc .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 其余服务(静态编译)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.5iot.com/iotlcm-public/iotlcm-openapi.git
|
||||||
|
cd iotlcm-openapi
|
||||||
|
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -trimpath \
|
||||||
|
-ldflags "-s -w \
|
||||||
|
-X main.BuildVersion=$(git describe --tags --always) \
|
||||||
|
-X main.CommitID=$(git rev-parse --short HEAD) \
|
||||||
|
-X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||||
|
-o iotlcm-openapi .
|
||||||
|
```
|
||||||
|
|
||||||
|
替换仓库名与产物名即可用于其他服务,`bot` 需要指定子目录:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd iotlcm-bot
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o iotlcm-bot ./cmd/iotlcm-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
### 交叉编译说明
|
||||||
|
|
||||||
|
| 服务 | 能否交叉编译 |
|
||||||
|
| --- | --- |
|
||||||
|
| admin-svr(CGO) | ❌ 需在目标架构上编译,或用对应架构的交叉工具链 |
|
||||||
|
| 其余(静态) | ✅ 可直接 `GOOS=linux GOARCH=arm64` |
|
||||||
|
|
||||||
|
查看服务器架构:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uname -m # x86_64 → amd64;aarch64 → arm64
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署布局
|
||||||
|
|
||||||
|
```text
|
||||||
|
/opt/iotlcm/
|
||||||
|
├── admin-svr/
|
||||||
|
│ ├── iotlcmadmin_svc
|
||||||
|
│ ├── config/config.yaml ← 从 config/admin-svr 复制
|
||||||
|
│ ├── logs/
|
||||||
|
│ ├── upload/
|
||||||
|
│ └── cache/
|
||||||
|
├── openapi/
|
||||||
|
│ ├── iotlcm-openapi
|
||||||
|
│ ├── config/config.yaml
|
||||||
|
│ └── logs/
|
||||||
|
├── worker-svr/
|
||||||
|
│ ├── iotlcm-worker-svr
|
||||||
|
│ ├── config/config.yaml
|
||||||
|
│ └── logs/
|
||||||
|
├── upstream-chan/
|
||||||
|
├── upstream-proxy/
|
||||||
|
├── app-server/
|
||||||
|
│ ├── iotlcm-app-server
|
||||||
|
│ ├── config/config.yaml
|
||||||
|
│ └── resource/
|
||||||
|
│ ├── log/server/
|
||||||
|
│ └── upload/
|
||||||
|
├── sms/
|
||||||
|
└── bot/
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置文件放 `config/config.yaml`**(相对工作目录),这是 GoFrame 的默认搜索路径。启动时工作目录必须在服务目录内。
|
||||||
|
|
||||||
|
### 目录准备
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt/iotlcm/{admin-svr,openapi,worker-svr,upstream-chan,upstream-proxy,app-server,sms,bot}
|
||||||
|
sudo useradd -r -s /sbin/nologin iotlcm
|
||||||
|
sudo chown -R iotlcm:iotlcm /opt/iotlcm
|
||||||
|
```
|
||||||
|
|
||||||
|
## 修改配置中的服务地址
|
||||||
|
|
||||||
|
容器部署时配置里写的是服务名(`mysql:3306`、`upstream-proxy:8510`),二进制部署必须改为实际地址:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# admin-svr / openapi / worker-svr / app-server
|
||||||
|
database:
|
||||||
|
default:
|
||||||
|
link: "mysql:u_iotlcm:<口令>@tcp(127.0.0.1:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
|
||||||
|
redis:
|
||||||
|
default:
|
||||||
|
address: "127.0.0.1:6379"
|
||||||
|
rabbitmq:
|
||||||
|
dsn: "amqp://<用户>:<口令>@127.0.0.1:5672/"
|
||||||
|
|
||||||
|
# upstream-chan / upstream-proxy
|
||||||
|
registry:
|
||||||
|
address: "127.0.0.1:8500"
|
||||||
|
|
||||||
|
# 上游服务地址
|
||||||
|
upstream:
|
||||||
|
proxy:
|
||||||
|
address: "127.0.0.1:8510"
|
||||||
|
gateway:
|
||||||
|
address: "127.0.0.1:8110"
|
||||||
|
```
|
||||||
|
|
||||||
|
::: danger logger.path 也要改
|
||||||
|
配置里的 `logger.path` 在容器版本中是 `/app/logs`,二进制部署要改为实际路径,例如 `/opt/iotlcm/admin-svr/logs`,否则会因目录不存在启动失败。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## systemd 托管
|
||||||
|
|
||||||
|
以为 `admin-svr` 为例,保存为 `/etc/systemd/system/iotlcm-admin-svr.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=IotLCM Admin Service
|
||||||
|
After=network-online.target mysql.service redis.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=iotlcm
|
||||||
|
Group=iotlcm
|
||||||
|
WorkingDirectory=/opt/iotlcm/admin-svr
|
||||||
|
ExecStart=/opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5s
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
# 环境变量(JWT 密钥等敏感值建议放独立文件)
|
||||||
|
EnvironmentFile=-/etc/iotlcm/admin-svr.env
|
||||||
|
|
||||||
|
# 日志交给 journald,也可用配置里的文件日志
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
# 安全加固
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=true
|
||||||
|
ReadWritePaths=/opt/iotlcm/admin-svr
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
`/etc/iotlcm/admin-svr.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JWT_SIGNING_KEY=<与其它服务一致的密钥>
|
||||||
|
```
|
||||||
|
|
||||||
|
启用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now iotlcm-admin-svr
|
||||||
|
sudo systemctl status iotlcm-admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
### 各服务的差异点
|
||||||
|
|
||||||
|
| 服务 | 特殊处理 |
|
||||||
|
| --- | --- |
|
||||||
|
| admin-svr | 需 `ReadWritePaths` 包含 upload 与 logs 目录 |
|
||||||
|
| worker-svr | 无监听端口,`After` 需包含 rabbitmq |
|
||||||
|
| upstream-chan / proxy | `After` 需包含 consul |
|
||||||
|
| app-server | 需 `resource/log/server` 与 `resource/upload` 可写 |
|
||||||
|
| bot | `ExecStart` 需带参数:`/opt/iotlcm/bot/iotlcm-bot watch --config /opt/iotlcm/bot/config/config.yaml` |
|
||||||
|
|
||||||
|
### 启动顺序
|
||||||
|
|
||||||
|
```text
|
||||||
|
MySQL / Redis / RabbitMQ / Consul
|
||||||
|
↓
|
||||||
|
admin-svr、openapi、worker-svr、upstream-chan、upstream-proxy、app-server、sms
|
||||||
|
↓
|
||||||
|
admin-ui、app-web、docs(静态文件,由 nginx 托管)
|
||||||
|
```
|
||||||
|
|
||||||
|
用 systemd 依赖表达:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
After=iotlcm-admin-svr.service
|
||||||
|
Requires=iotlcm-admin-svr.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 静态前端
|
||||||
|
|
||||||
|
`admin-ui`、`app-web`、`docs` 是静态文件,二进制部署时不需要跑容器,把构建产物交给 nginx:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# admin-ui:需 Node 构建
|
||||||
|
cd iotlcm-admin-ui/vue
|
||||||
|
npm ci && npm run build
|
||||||
|
sudo cp -r apps/web-antd/dist /var/www/iotlcm/admin-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
nginx 配置参考部署仓的 `config/admin-ui/default.conf` 与 `config/nginx/iotlcm.conf`,把 `proxy_pass http://admin-svr:8008` 改为 `http://127.0.0.1:8008`。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 进程状态
|
||||||
|
systemctl status iotlcm-admin-svr
|
||||||
|
journalctl -u iotlcm-admin-svr -n 50 --no-pager
|
||||||
|
|
||||||
|
# 端口监听
|
||||||
|
ss -lntp | grep -E '8008|8000|8510|8110'
|
||||||
|
|
||||||
|
# 接口
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8008/api.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 与容器部署的差异
|
||||||
|
|
||||||
|
| 项 | 容器部署 | 二进制部署 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 配置路径 | `/app/config`(挂载) | `<服务目录>/config` |
|
||||||
|
| 日志路径 | `/app/logs` | 需在配置里改成实际路径 |
|
||||||
|
| 服务互访 | 服务名(Docker DNS) | `127.0.0.1` 或内网 IP |
|
||||||
|
| 进程管理 | Docker restart policy | systemd |
|
||||||
|
| 升级 | 换镜像 + `up -d` | 停服务 → 换二进制 → 起服务 |
|
||||||
|
| 时区 | 镜像已内置 | 依赖宿主机 tzdata |
|
||||||
|
|
||||||
|
## 升级流程
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 备份配置与程序
|
||||||
|
sudo tar czf /backup/iotlcm-admin-svr-$(date +%F).tar.gz -C /opt/iotlcm admin-svr
|
||||||
|
|
||||||
|
# 2. 停服务
|
||||||
|
sudo systemctl stop iotlcm-admin-svr
|
||||||
|
|
||||||
|
# 3. 替换二进制
|
||||||
|
sudo cp iotlcmadmin_svc /opt/iotlcm/admin-svr/
|
||||||
|
sudo chown iotlcm:iotlcm /opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||||
|
|
||||||
|
# 4. 起服务并验证
|
||||||
|
sudo systemctl start iotlcm-admin-svr
|
||||||
|
sudo systemctl status iotlcm-admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回滚
|
||||||
|
|
||||||
|
保留上一版二进制,回滚只需换回:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop iotlcm-admin-svr
|
||||||
|
sudo cp /opt/iotlcm/admin-svr/iotlcmadmin_svc.bak /opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||||
|
sudo systemctl start iotlcm-admin-svr
|
||||||
|
```
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
---
|
||||||
|
title: 自建镜像
|
||||||
|
description: 从源码构建各服务的 Docker 镜像
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 自建镜像
|
||||||
|
|
||||||
|
平台镜像由官方仓库分发。如有定制需求(改代码、加依赖、适配私有基础镜像),可从源码自行构建。
|
||||||
|
|
||||||
|
## 构建环境
|
||||||
|
|
||||||
|
| 项 | 要求 |
|
||||||
|
| --- | --- |
|
||||||
|
| Docker | 20.10 以上 |
|
||||||
|
| 网络 | 需访问基础镜像仓库与 Go/npm 依赖源 |
|
||||||
|
| 磁盘 | 单服务构建约 1~2 GB(含构建缓存) |
|
||||||
|
|
||||||
|
## 各服务构建文件
|
||||||
|
|
||||||
|
| 服务 | 仓库 | Dockerfile | 构建上下文 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| admin-svr | `iotlcm-admin-svr` | `server/build/Dockerfile` | `server/` |
|
||||||
|
| openapi | `iotlcm-openapi` | `build/Dockerfile` | 仓库根目录 |
|
||||||
|
| worker-svr | `iotlcm-worker-svr` | `build/Dockerfile` | 仓库根目录 |
|
||||||
|
| upstream-chan | `iotlcm-upstream-chan` | `build/Dockerfile` | 仓库根目录 |
|
||||||
|
| upstream-proxy | `iotlcm-upstream-proxy` | `build/Dockerfile` | 仓库根目录 |
|
||||||
|
| app-server | `iotlcm-app` | `server/Dockerfile` | `server/` |
|
||||||
|
| app-web | `iotlcm-app` | `app/Dockerfile` | `app/` |
|
||||||
|
| admin-ui | `iotlcm-admin-ui` | `vue/build/Dockerfile` | `vue/` |
|
||||||
|
| sms | `iotlcm-sms` | `build/Dockerfile` | 仓库根目录 |
|
||||||
|
| bot | `iotlcm-bot` | `Dockerfile` | 仓库根目录 |
|
||||||
|
| docs | `iotlcm-docs` | `Dockerfile` | 仓库根目录 |
|
||||||
|
| onelink-mock | `iotlcm-onelink-mock` | `Dockerfile` | 仓库根目录 |
|
||||||
|
|
||||||
|
## ⚠️ 先处理私有基础镜像
|
||||||
|
|
||||||
|
**当前部分仓库的 Dockerfile 仍指向私有镜像仓库**,外部环境无法拉取,直接构建会失败:
|
||||||
|
|
||||||
|
| 仓库 | 文件 | 问题 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `iotlcm-openapi` | `manifest/docker/Dockerfile` | `FROM` 指向私有仓库 |
|
||||||
|
| `iotlcm-app` | `app/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||||
|
| `iotlcm-app` | `server/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||||
|
| `iotlcm-admin-ui` | `vue/build/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||||
|
| `iotlcm-admin-ui` | `vue/build/DockerfileNoBuild` | `FROM` 指向私有仓库 |
|
||||||
|
|
||||||
|
构建前把这些 `FROM` 改为公共镜像:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 修改前(私有仓库,外部不可达)
|
||||||
|
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/golangalpine:1.25.7-alpine3.23 AS builder
|
||||||
|
|
||||||
|
# 修改后(公共镜像)
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
```
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/nginx:1.30.0-alpine
|
||||||
|
|
||||||
|
# 改为
|
||||||
|
FROM nginx:1.30-alpine
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 其余仓库的 Dockerfile 已可用
|
||||||
|
`admin-svr`、`worker-svr`、`upstream-chan`、`upstream-proxy`、`sms`、`bot`、`docs`、`onelink-mock` 的 Dockerfile 基础镜像均为公共镜像,可直接构建。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 构建命令
|
||||||
|
|
||||||
|
### 后端服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
VERSION=$(git describe --tags --always)
|
||||||
|
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
--build-arg BuildVersion=$VERSION \
|
||||||
|
--build-arg CommitID=$COMMIT \
|
||||||
|
--build-arg BuildTime=$BUILD_TIME \
|
||||||
|
-t your-registry/iotlcm/iotlcm:iotlcm-openapi-$VERSION \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
`admin-svr` 与 `app-server` 需指定上下文目录:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd iotlcm-admin-svr
|
||||||
|
docker build \
|
||||||
|
--build-arg BuildVersion=$VERSION \
|
||||||
|
--build-arg CommitID=$COMMIT \
|
||||||
|
--build-arg BuildTime=$BUILD_TIME \
|
||||||
|
-t your-registry/iotlcm/iotlcm:iotlcm-admin-svr-$VERSION \
|
||||||
|
-f server/build/Dockerfile server/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd iotlcm-admin-ui/vue
|
||||||
|
docker build -t your-registry/iotlcm/iotlcm:iotlcm-admin-ui-$VERSION -f build/Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd iotlcm-app/app
|
||||||
|
docker build -t your-registry/iotlcm/iotlcm:iotlcm-app-web-$VERSION .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构建参数说明
|
||||||
|
|
||||||
|
| 参数 | 用途 |
|
||||||
|
| --- | --- |
|
||||||
|
| `BuildVersion` | 写入二进制,可通过版本接口查询 |
|
||||||
|
| `CommitID` | 提交号,便于追溯 |
|
||||||
|
| `BuildTime` | 构建时间 |
|
||||||
|
|
||||||
|
`openapi`、`worker-svr` 等仓库支持用 `GOPROXY` 构建参数替换代理:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
|
||||||
|
```
|
||||||
|
|
||||||
|
## 镜像命名规则
|
||||||
|
|
||||||
|
官方仓库把**所有镜像放在同一个镜像仓库** `iotlcm/iotlcm` 下,靠 tag 区分服务与版本:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<Registry>/iotlcm/iotlcm:<服务名>-<版本>
|
||||||
|
```
|
||||||
|
|
||||||
|
| 服务 | tag | 完整示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 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` |
|
||||||
|
| openapi | `iotlcm-openapi-<TAG>` | `.../iotlcm/iotlcm:iotlcm-openapi-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.2` |
|
||||||
|
| 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` |
|
||||||
|
| worker-svr | `iotlcm-worker-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4` |
|
||||||
|
| sms | `iotlcm-sms-<TAG>` | `.../iotlcm/iotlcm:iotlcm-sms-v1.0.4` |
|
||||||
|
| bot | `iotlcm-bot-<TAG>` | `.../iotlcm/iotlcm:iotlcm-bot-v1.0.4` |
|
||||||
|
| docs | `iotlcm-docs-<TAG>` | `.../iotlcm/iotlcm:iotlcm-docs-v1.0.4` |
|
||||||
|
| onelink-mock | `iotlcm-onelink-mock-<TAG>` | `.../iotlcm/iotlcm:iotlcm-onelink-mock-v1.0.4` |
|
||||||
|
|
||||||
|
基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,从上游同步到同一仓库即可,tag 为 `<组件>-<版本>`,见下节。
|
||||||
|
|
||||||
|
::: warning tag 必须带上服务名
|
||||||
|
同一个仓库里 tag 不能重复。只用版本号(如 `v1.0.4`)会让 12 个服务互相覆盖,因此 tag 固定为 `<服务名>-<版本>`。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 同步基础组件
|
||||||
|
|
||||||
|
平台编排不再从 Docker Hub 直连基础组件(公网不稳定,且 `8.0` 这类 tag 会随补丁升级而漂)。部署前先把五个镜像同步到官方仓库:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
REPO=crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com
|
||||||
|
|
||||||
|
docker login --username=sdqmx $REPO
|
||||||
|
|
||||||
|
# 按 docker-compose.yml 中固定的版本拉取并推送
|
||||||
|
sync_image() {
|
||||||
|
local src=$1 dst=$2
|
||||||
|
docker pull "$src"
|
||||||
|
docker tag "$src" "$REPO/iotlcm/iotlcm:$dst"
|
||||||
|
docker push "$REPO/iotlcm/iotlcm:$dst"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 参数:<上游镜像> <目标 tag(组件名-版本)>
|
||||||
|
sync_image mysql:8.0 mysql-8.0
|
||||||
|
sync_image redis:7.2 redis-7.2
|
||||||
|
sync_image rabbitmq:4.3.5-management rabbitmq-4.3.5-management
|
||||||
|
sync_image hashicorp/consul:1.22 consul-1.22
|
||||||
|
sync_image nginx:1.30 nginx-1.30
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning 同步时把版本号固定到补丁位
|
||||||
|
`mysql:8.0` 这类浮动 tag 会随上游发新补丁而变化。建议同步时锁定到具体补丁版(如 `mysql:8.0.43`),tag 写成 `mysql-8.0.43`,并把 `docker-compose.yml` 里对应的 `image` 同步改掉,保证多台机器拉到的镜像完全一致。
|
||||||
|
:::
|
||||||
|
|
||||||
|
同步完成后校验:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for tag in mysql-8.0 redis-7.2 rabbitmq-4.3.5-management consul-1.22 nginx-1.30; do
|
||||||
|
docker manifest inspect "$REPO/iotlcm/iotlcm:$tag" >/dev/null 2>&1 \
|
||||||
|
&& echo "✅ $tag" || echo "❌ $tag"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 推送到自己的仓库
|
||||||
|
|
||||||
|
自建镜像不需要推送回官方仓库。推到自有仓库后,把 `docker-compose.yml` 里的镜像前缀改指向它:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
REG=harbor.internal.example.com
|
||||||
|
TAG=1.0.3
|
||||||
|
|
||||||
|
docker tag iotlcm-openapi:local $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
||||||
|
docker login $REG
|
||||||
|
docker push $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml —— 逐个替换 image 前缀
|
||||||
|
image: 'harbor.internal.example.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 只覆盖部分服务
|
||||||
|
只想替换其中一两个服务时,把对应镜像推到自有仓库,其余保留官方地址。此时把 `docker-compose.yml` 里那几个 `image` 单独写成完整地址,其余保持官方仓库前缀即可。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 批量构建脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO=${REPO:?请设置 REPO}
|
||||||
|
TAG=${TAG:-$(date +%Y%m%d)}
|
||||||
|
WORKDIR=${WORKDIR:-./repos}
|
||||||
|
|
||||||
|
# 服务:仓库:上下文:产物镜像名
|
||||||
|
SERVICES=(
|
||||||
|
"admin-svr:iotlcm-admin-svr:server/:iotlcm-admin-svr"
|
||||||
|
"openapi:iotlcm-openapi:.:iotlcm-openapi"
|
||||||
|
"worker-svr:iotlcm-worker-svr:.:iotlcm-worker-svr"
|
||||||
|
"upstream-chan:iotlcm-upstream-chan:.:iotlcm-upstream-chan"
|
||||||
|
"upstream-proxy:iotlcm-upstream-proxy:.:iotlcm-upstream-proxy"
|
||||||
|
"app-server:iotlcm-app:server/:iotlcm-app-server"
|
||||||
|
"sms:iotlcm-sms:.:iotlcm-sms"
|
||||||
|
"bot:iotlcm-bot:.:iotlcm-bot"
|
||||||
|
)
|
||||||
|
|
||||||
|
mkdir -p "$WORKDIR"
|
||||||
|
|
||||||
|
for item in "${SERVICES[@]}"; do
|
||||||
|
IFS=':' read -r name repo ctx image <<< "$item"
|
||||||
|
echo "=== 构建 $name ==="
|
||||||
|
|
||||||
|
if [ ! -d "$WORKDIR/$repo" ]; then
|
||||||
|
git clone "https://git.5iot.com/iotlcm-public/$repo.git" "$WORKDIR/$repo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$(git -C "$WORKDIR/$repo" describe --tags --always 2>/dev/null || echo unknown)
|
||||||
|
COMMIT=$(git -C "$WORKDIR/$repo" rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||||
|
|
||||||
|
find "$WORKDIR/$repo" -name Dockerfile -exec \
|
||||||
|
sed -i 's#FROM crpi-[^ ]*/\([a-z]*\):\([0-9.]*-alpine[0-9.]*\)#FROM \1:\2#' {} \;
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
--build-arg BuildVersion="$VERSION" \
|
||||||
|
--build-arg CommitID="$COMMIT" \
|
||||||
|
--build-arg BuildTime="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||||
|
-t "$REPO/iotlcm/$image:$TAG" \
|
||||||
|
-f "$WORKDIR/$repo/${ctx}Dockerfile" \
|
||||||
|
"$WORKDIR/$repo/$ctx"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "全部完成,镜像标签:$REPO/iotlcm/*:$TAG"
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning 脚本里的 sed 只能应急
|
||||||
|
上面的 `sed` 是粗略替换,把私有仓库地址映射为公共镜像名。仅适用于地址格式规整的情况,用前先 `git diff` 确认改对了。正式做法应把 Dockerfile 的修正提交回仓库。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 验证构建结果
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 镜像大小
|
||||||
|
docker images | grep iotlcm
|
||||||
|
|
||||||
|
# 二进制能否启动
|
||||||
|
docker run --rm your-registry/iotlcm/iotlcm:iotlcm-openapi-$TAG --help 2>&1 | head
|
||||||
|
|
||||||
|
# 检查架构
|
||||||
|
docker inspect your-registry/iotlcm/iotlcm:iotlcm-openapi-$TAG \
|
||||||
|
--format '{{.Architecture}}/{{.Os}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
构建后按[启动与验证](install)的清单做一次完整部署验证,不要只看镜像是否构建成功。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 现象 | 原因 | 处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `pull access denied` / `unauthorized` | Dockerfile 里是私有仓库地址 | 按上文替换 `FROM` |
|
||||||
|
| `go mod download` 超时 | 网络无法访问 Go 模块代理 | 加 `--build-arg GOPROXY=https://goproxy.cn,direct` |
|
||||||
|
| `npm ci` 失败 | 前端仓库 lock 文件与 package.json 不一致 | 改用 `npm install`,并注意提交 lock 变更 |
|
||||||
|
| 镜像体积过大 | 未使用多阶段构建,或缓存未清理 | 确认用的是仓库自带的 Dockerfile(均为多阶段) |
|
||||||
|
| 容器启动后立即退出 | 镜像内缺少配置文件 | 容器认为 `config/config.yaml` 必须存在,需挂载或用占位配置 |
|
||||||
|
| `exec format error` | 构建架构与运行架构不一致 | 加 `--platform linux/amd64` |
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
---
|
||||||
|
title: 配置说明
|
||||||
|
description: 部署配置的结构、默认值与修改方式
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 配置说明
|
||||||
|
|
||||||
|
平台配置分两层,**都随仓库分发、都带可用默认值**,无需复制或初始化即可启动:
|
||||||
|
|
||||||
|
| 层 | 位置 | 管什么 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 编排层 | `docker-compose.yml` | 镜像地址、端口、数据库/Redis/MQ 口令、容器编排 |
|
||||||
|
| 应用层 | `config/<服务>/config.yaml` | 业务参数、连接串、功能开关 |
|
||||||
|
|
||||||
|
::: warning 配置里是字面值,不是环境变量
|
||||||
|
配置文件与编排文件里直接写的是具体值,**不使用 `${VAR}` 占位**。
|
||||||
|
|
||||||
|
原因:GoFrame 不会展开配置文件中的 `${VAR}`(环境变量只用于覆盖配置键,不参与文件内文本替换),用占位符会导致连接串变成字面量 `${X}`,服务直接连不上库。
|
||||||
|
|
||||||
|
代价是改口令要改多处,见下方「改口令的正确姿势」。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 编排层:`docker-compose.yml`
|
||||||
|
|
||||||
|
### 镜像地址
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: 'crpi-.../iotlcm/iotlcm:mysql-8.0'
|
||||||
|
image: 'crpi-.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4'
|
||||||
|
```
|
||||||
|
|
||||||
|
所有镜像(基础组件 + 业务服务)都在**同一个镜像仓库** `<Registry>/iotlcm/iotlcm` 下,靠 tag 区分:
|
||||||
|
|
||||||
|
| tag 形式 | 示例 |
|
||||||
|
| --- | --- |
|
||||||
|
| 基础组件:`<组件>-<版本>` | `mysql-8.0`、`redis-7.2`、`nginx-1.30` |
|
||||||
|
| 业务服务:`<服务>-<版本>` | `iotlcm-admin-svr-v1.0.4`、`iotlcm-app-server-v1.0.2` |
|
||||||
|
|
||||||
|
改用自有仓库时,全局替换 Registry 前缀即可。
|
||||||
|
|
||||||
|
### 默认口令
|
||||||
|
|
||||||
|
| 变量 | 默认值 | 用途 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `MYSQL_ROOT_PASSWORD` | 见文件 | MySQL root |
|
||||||
|
| `MYSQL_DATABASE` | `db_iotlcm` | 库名 |
|
||||||
|
| `RABBITMQ_DEFAULT_PASS` | 见文件 | RabbitMQ |
|
||||||
|
| redis `--requirepass` | 见文件 | Redis |
|
||||||
|
|
||||||
|
### 端口
|
||||||
|
|
||||||
|
| 端口 | 服务 | 对外 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 8008 | admin-svr | 经反向代理 |
|
||||||
|
| 8009 | admin-ui | **浏览器直接访问这个** |
|
||||||
|
| 8000 | openapi | 经反向代理 |
|
||||||
|
| 8010 / 8018 | app-server / app-web | 经反向代理 |
|
||||||
|
| 8019 | docs | 经反向代理 |
|
||||||
|
| 8080 | onelink-mock | 仅联调 |
|
||||||
|
| 3306 / 6379 / 5672 / 8500 | 基础组件 | 仅内网 |
|
||||||
|
| 8110 / 8310 / 8510 | chan / proxy | 仅内网 |
|
||||||
|
| 11190 / 11200 | sms | 仅内网 |
|
||||||
|
|
||||||
|
改端口直接改 `docker-compose.yml` 中对应 `ports` 的冒号左侧。
|
||||||
|
|
||||||
|
## 应用层:`config/`
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 各服务要点
|
||||||
|
|
||||||
|
#### admin-svr(管理端后端)
|
||||||
|
|
||||||
|
| 项 | 默认情况 |
|
||||||
|
| --- | --- |
|
||||||
|
| `database.default.link` | 指向 `mysql:3306`,口令与应用账号一致 |
|
||||||
|
| `database.legacyCard.link` | 空,不使用旧平台库 |
|
||||||
|
| `jwt.signingKey` | 已填固定密钥,需与 openapi、app-server 一致 |
|
||||||
|
| `warning.sms.enabled` | `true`,未配短信服务商时建议改 `false` |
|
||||||
|
| `warning.wechat.enabled` | `false` |
|
||||||
|
| `register.email.*` | 空,不发邮件 |
|
||||||
|
| `tsdb.tdengine.dsn` | 空,不启用时序库 |
|
||||||
|
| `alipay.*` | 空,不启用支付 |
|
||||||
|
| `upstream.appDiagnosisToken` | 已填,需与 upstream-proxy 一致 |
|
||||||
|
|
||||||
|
#### openapi(对外 API)
|
||||||
|
|
||||||
|
`jwt.signingKey` 与 admin-svr 一致;`database.default.link` 同库。建议另建只读账号用于生产。
|
||||||
|
|
||||||
|
#### worker-svr(异步任务)
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `batch.inbound.upstreamConcurrency` | 默认 300,机器弱或上游配额小时调低 |
|
||||||
|
| `batch.inbound.upstreamRateLimit` | 运营商限流,**必须按上游实际配额设置**,调大易被封禁 |
|
||||||
|
| `tsdb.tdengine.dsn` | 空,不启用时序库 |
|
||||||
|
|
||||||
|
#### upstream-chan(运营商对接)
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `registry.address` | `consul:8500` |
|
||||||
|
| `dependencies.sim_yd_mock_v5.url` | 指向挡板 `http://onelink-mock:8080/v5/`,接真实运营商时删除本段 |
|
||||||
|
|
||||||
|
运营商通道本身(AppKey、AppSecret、接口地址)在平台「通道管理」中录入,存数据库,不在本文件。
|
||||||
|
|
||||||
|
#### upstream-proxy(通道缓存转发)
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `appDiagnosis.internalToken` | 与 admin-svr 的 `upstream.appDiagnosisToken` 一致 |
|
||||||
|
| `gateway.batchTimeout` | 默认 20m,**worker 侧超时必须大于此值** |
|
||||||
|
|
||||||
|
#### app-server(小程序后端)
|
||||||
|
|
||||||
|
`jwt.signingKey` 同上;`payment.wechat.enabled` 默认 `false`;`appCardRecognition.enabled` 默认 `false`(拍照识别需自备视觉模型 API)。
|
||||||
|
|
||||||
|
#### sms(短信服务)
|
||||||
|
|
||||||
|
| 项 | 默认 |
|
||||||
|
| --- | --- |
|
||||||
|
| `serviceType` | `1`(阿里云) |
|
||||||
|
| `accessKeyId` / `accessKeySecret` | 空,需自行填写 |
|
||||||
|
| `signName` | `示例签名` |
|
||||||
|
|
||||||
|
不填则无法发送验证码,其余功能正常。
|
||||||
|
|
||||||
|
#### bot(飞书通知)
|
||||||
|
|
||||||
|
`feishu.appId` / `appSecret` / `chatId` 均为空。不启动该容器即可。
|
||||||
|
|
||||||
|
#### admin-ui / nginx
|
||||||
|
|
||||||
|
`admin-ui/default.conf` 把 `/api/` 反代到 `admin-svr:8008`;`nginx/iotlcm.conf` 按域名分流,需把 `your-domain.example.com` 换成实际域名。
|
||||||
|
|
||||||
|
## 改口令的正确姿势
|
||||||
|
|
||||||
|
改数据库口令时,以下**三处必须同时改**,漏一处就起不来:
|
||||||
|
|
||||||
|
| 位置 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| `docker-compose.yml` | `MYSQL_ROOT_PASSWORD`、redis `--requirepass`、`RABBITMQ_DEFAULT_PASS` |
|
||||||
|
| `config/*/config.yaml` | 各服务的 `database.default.link`、`redis.default.pass`、`rabbitmq.dsn` |
|
||||||
|
| `schema/04-users.sh`、`schema/05-reset-passwords.sh` | 脚本内的 `APP_PASSWORD` / `ADMIN_PASSWORD` / `ROOT_PASSWORD` |
|
||||||
|
|
||||||
|
::: tip 一次性全局替换
|
||||||
|
```bash
|
||||||
|
OLD='原口令'
|
||||||
|
NEW='新口令'
|
||||||
|
|
||||||
|
# 编排 + 配置
|
||||||
|
grep -rl "$OLD" docker-compose.yml config/ | xargs sed -i "s/$OLD/$NEW/g"
|
||||||
|
# 初始化脚本
|
||||||
|
grep -rl "$OLD" schema/ | xargs sed -i "s/$OLD/$NEW/g"
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: danger 口令不能含 @ : / 等字符
|
||||||
|
连接串格式为 `mysql:用户:口令@tcp(主机:端口)/库`,口令里的 `@`、`:`、`/` 会被解析成分隔符,导致连接失败。用字母数字组合。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 配置自检
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 编排语法
|
||||||
|
docker compose config --quiet
|
||||||
|
|
||||||
|
# 看某个服务最终的配置展开
|
||||||
|
docker compose config | sed -n '/admin-svr:/,/^ [a-z]/p'
|
||||||
|
```
|
||||||
|
|
||||||
|
配置语法错误会导致服务启动后立刻退出,看日志定位:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs --tail=50 admin-svr
|
||||||
|
```
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
title: 部署概览
|
||||||
|
description: IotLCM 平台的部署方式、构成与前置条件
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 部署概览
|
||||||
|
|
||||||
|
IotLCM 平台通过 Docker Compose 在单机上部署。部署编排与数据库初始化脚本在独立仓库:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.5iot.com/iotlcm-public/iotlcm-deploy.git
|
||||||
|
cd iotlcm-deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
iotlcm-deploy/
|
||||||
|
├── docker-compose.yml # 服务编排(含默认口令与端口)
|
||||||
|
├── config/ # 各服务配置,10 份,已带可用默认值
|
||||||
|
└── schema/ # 数据库初始化脚本,13 个,首次启动自动执行
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署内容
|
||||||
|
|
||||||
|
| 类别 | 服务 |
|
||||||
|
| --- | --- |
|
||||||
|
| 基础组件 | MySQL、Redis、RabbitMQ、Consul |
|
||||||
|
| 管理端 | admin-svr(后端)、admin-ui(前端) |
|
||||||
|
| 对外接口 | openapi |
|
||||||
|
| 小程序 / H5 | app-server(后端)、app-web(前端) |
|
||||||
|
| 运营商对接 | upstream-chan(协议适配)、upstream-proxy(缓存转发) |
|
||||||
|
| 异步任务 | worker-svr(批次执行、全量同步) |
|
||||||
|
| 配套 | sms(短信)、docs(本文档站) |
|
||||||
|
| 可选 | onelink-mock(运营商接口挡板)、bot(飞书通知)、nginx(反向代理) |
|
||||||
|
|
||||||
|
## 部署流程
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <部署仓地址> iotlcm-deploy
|
||||||
|
cd iotlcm-deploy
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
三步,不需要复制或修改任何文件——配置与编排都随仓库分发且带可用默认值。首次启动自动建库并导入 schema,约 1~2 分钟。
|
||||||
|
|
||||||
|
启动后访问 `http://<宿主机IP>:8009`,账号 `admin`,口令见 `docker-compose.yml`。
|
||||||
|
|
||||||
|
::: warning 默认口令只用于试用
|
||||||
|
正式部署前必须修改数据库、Redis、RabbitMQ、admin 账号口令与 JWT 密钥,详见[配置说明](configuration)。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 文档导航
|
||||||
|
|
||||||
|
| 文档 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| [环境准备](prerequisites) | 硬件、系统、Docker 版本、镜像仓库、端口规划 |
|
||||||
|
| [配置说明](configuration) | `docker-compose.yml` 与 `config/` 各项含义 |
|
||||||
|
| [启动与验证](install) | 启动流程、验证清单、首次登录 |
|
||||||
|
| [其他部署方式](alternatives) | 二进制、自建镜像、分离、离线 |
|
||||||
|
| [可选服务](optional) | 接口挡板、飞书通知、反向代理 |
|
||||||
|
| [升级与备份](operations) | 版本固定、升级回滚、数据备份 |
|
||||||
|
| [故障排查](troubleshooting) | 常见问题与处理 |
|
||||||
|
|
||||||
|
## 关键约束
|
||||||
|
|
||||||
|
::: warning JWT 密钥必须三处一致
|
||||||
|
`admin-svr`、`openapi`、`app-server` 共用同一个 `JWT_SIGNING_KEY`。不一致的表现是能登录管理端,但调用外部 API 或小程序端提示未授权。
|
||||||
|
|
||||||
|
更换该值会使已签发的全部登录态与接口令牌立即失效,需在维护窗口操作。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: tip 镜像标签不要用 latest
|
||||||
|
生产环境必须把 `TAG` 固定到具体版本号。`latest` 会随仓库推送变化,同一份编排在不同时间拉到的镜像不同,出问题无法定位,也无法明确回滚。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: danger 修改 schema 脚本不会自动生效
|
||||||
|
数据库初始化只在 `data/mysql` 为空时执行。改了 `schema/` 下的脚本必须清空数据卷才会重跑,而清空会丢失全部数据。生产环境禁止这样操作。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 服务端口
|
||||||
|
|
||||||
|
默认端口规划:
|
||||||
|
|
||||||
|
| 端口 | 服务 | 是否对外 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 8000 | openapi | 经反向代理 |
|
||||||
|
| 8008 | admin-svr | 经反向代理 |
|
||||||
|
| 8009 | admin-ui | 经反向代理 |
|
||||||
|
| 8010 | app-server | 经反向代理 |
|
||||||
|
| 8018 | app-web | 经反向代理 |
|
||||||
|
| 8019 | docs | 经反向代理 |
|
||||||
|
| 3306 / 6379 / 5672 / 8500 | MySQL / Redis / RabbitMQ / Consul | 仅内网 |
|
||||||
|
| 8110 / 8310 / 8510 | upstream-chan / upstream-proxy | 仅内网 |
|
||||||
|
| 11190 / 11200 | sms | 仅内网 |
|
||||||
|
| 8080 | onelink-mock | 仅联调 |
|
||||||
|
|
||||||
|
端口冲突时修改 `docker-compose.yml` 中对应变量,不要直接改 `docker-compose.yml`。
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
---
|
||||||
|
title: 启动与验证
|
||||||
|
description: 首次启动流程、状态检查与验证清单
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 启动与验证
|
||||||
|
|
||||||
|
## 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <部署仓地址> iotlcm-deploy
|
||||||
|
cd iotlcm-deploy
|
||||||
|
|
||||||
|
# 首次部署建议前台启动,便于观察初始化过程
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
# 确认无误后转后台
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
不需要复制或修改任何文件,配置已带可用默认值。
|
||||||
|
|
||||||
|
### 首次启动发生什么
|
||||||
|
|
||||||
|
1. **MySQL 建库并导入 schema**(约 1~2 分钟)
|
||||||
|
`schema/` 下 13 个脚本按文件名顺序执行:建表 → 结构补齐 → 创建应用账号 → 重置种子账号口令 → 导入通道模板/字典/菜单等种子数据
|
||||||
|
2. MySQL 通过健康检查后,`admin-svr`、`openapi`、`worker-svr` 依次启动
|
||||||
|
3. 各服务向 Consul 注册
|
||||||
|
|
||||||
|
观察初始化进度:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
看到 `[initdb] 应用账号 u_iotlcm 已创建` 与 `[initdb] 种子账号口令已统一重置` 表示初始化完成。
|
||||||
|
|
||||||
|
::: warning 初始化失败需清空数据卷
|
||||||
|
MySQL 只在 `data/mysql` 为空时执行 `schema/`。若中途失败,改完脚本必须清空卷才会重跑:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
sudo rm -rf ./data/mysql
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
这会删除全部数据,仅限首次部署阶段使用。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 检查状态
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
期望结果:
|
||||||
|
|
||||||
|
| 服务 | 状态 |
|
||||||
|
| --- | --- |
|
||||||
|
| iotlcm-mysql | Up (healthy) |
|
||||||
|
| iotlcm-redis | Up (healthy) |
|
||||||
|
| iotlcm-rabbitmq | Up (healthy) |
|
||||||
|
| iotlcm-consul | Up |
|
||||||
|
| iotlcm-admin-svr | Up |
|
||||||
|
| iotlcm-admin-ui | Up |
|
||||||
|
| iotlcm-openapi | Up |
|
||||||
|
| iotlcm-upstream-chan | Up |
|
||||||
|
| iotlcm-upstream-proxy | Up |
|
||||||
|
| iotlcm-worker-svr | Up |
|
||||||
|
| iotlcm-app-server | Up |
|
||||||
|
| iotlcm-app-web | Up |
|
||||||
|
| iotlcm-sms | Up (healthy) |
|
||||||
|
| iotlcm-docs | Up |
|
||||||
|
|
||||||
|
出现 `Restarting` 或 `Exited` 需查日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs --tail=100 <服务名>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证清单
|
||||||
|
|
||||||
|
按顺序逐项确认,任一步失败先解决再继续。
|
||||||
|
|
||||||
|
### 1. 数据库表数量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e \
|
||||||
|
"SELECT COUNT(*) AS tables FROM information_schema.tables WHERE table_schema=\"db_iotlcm\";"'
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `tables` = **89**。
|
||||||
|
|
||||||
|
### 2. 种子数据
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e \
|
||||||
|
"SELECT (SELECT COUNT(*) FROM sys_menu) AS menus, (SELECT COUNT(*) FROM sys_user) AS users;"'
|
||||||
|
```
|
||||||
|
|
||||||
|
预期菜单约 **447** 条,账号 **18** 个。
|
||||||
|
|
||||||
|
### 3. 管理端后端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8008/api.json
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `200`。
|
||||||
|
|
||||||
|
### 4. 管理端前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8009/
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `200`。
|
||||||
|
|
||||||
|
### 5. 对外 API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/api.json
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `200`。
|
||||||
|
|
||||||
|
### 6. 短信服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `200`。
|
||||||
|
|
||||||
|
### 7. 浏览器登录
|
||||||
|
|
||||||
|
访问 `http://<宿主机IP>:8009`:
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 账号 | `admin` |
|
||||||
|
| 口令 | `docker-compose.yml` 中 admin 的默认口令(首次登录后立即修改) |
|
||||||
|
|
||||||
|
能进入首页工作台、左侧菜单完整显示即部署成功。
|
||||||
|
|
||||||
|
## 首次登录后必做
|
||||||
|
|
||||||
|
1. **修改 admin 口令**:右上角个人中心 → 安全设置
|
||||||
|
2. **处理其余种子账号**:`user02`、`2admin` 等 17 个账号默认与 admin 同一口令,不使用请停用或删除
|
||||||
|
3. **确认字典与菜单加载**:进入「卡信息」页,确认筛选下拉选项不为空
|
||||||
|
|
||||||
|
## 后端自检
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# admin-svr 是否连上数据库
|
||||||
|
docker compose logs admin-svr | grep -iE 'error|panic' | tail -20
|
||||||
|
|
||||||
|
# worker-svr 是否连上 RabbitMQ
|
||||||
|
docker compose logs worker-svr | tail -30
|
||||||
|
|
||||||
|
# upstream-chan 是否连上 Redis 与 Consul
|
||||||
|
docker compose logs upstream-chan | tail -30
|
||||||
|
```
|
||||||
|
|
||||||
|
正常日志不应出现 `connection refused`、`access denied`、`panic`。
|
||||||
|
|
||||||
|
## 停止与重启
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose stop # 停止,保留数据
|
||||||
|
docker compose restart # 重启全部
|
||||||
|
docker compose restart admin-svr # 重启单个
|
||||||
|
docker compose down # 停止并删除容器(./data 不受影响)
|
||||||
|
```
|
||||||
|
|
||||||
|
::: danger 不要执行 down -v
|
||||||
|
`-v` 会删除命名数据卷。本编排数据都在 `./data` 宿主机目录,但 `-v` 仍可能影响同项目的其他卷。停止服务用 `down` 即可。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
- 需要运营商挡板联调 → [可选服务](optional)
|
||||||
|
- 需要域名访问 → [可选服务](optional) 的反向代理章节
|
||||||
|
- 遇到报错 → [故障排查](troubleshooting)
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
---
|
||||||
|
title: 离线部署
|
||||||
|
description: 服务器无法访问镜像仓库时的部署方式
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 离线部署
|
||||||
|
|
||||||
|
服务器无法访问外部镜像仓库时使用。核心思路:**在有网机器上导出镜像 → 传到目标机 → 导入**。
|
||||||
|
|
||||||
|
## 适用场景
|
||||||
|
|
||||||
|
- 生产服务器在内网,不能访问外网
|
||||||
|
- 镜像仓库在内网不可达,或需要走审批才能拉取
|
||||||
|
- 需要把某一批镜像固化下来,保证多台机器部署一致
|
||||||
|
|
||||||
|
## 一、确定镜像清单
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose config --images | sort -u
|
||||||
|
```
|
||||||
|
|
||||||
|
输出示例(含可选 profile 时用 `--profile` 指定):
|
||||||
|
|
||||||
|
```text
|
||||||
|
registry.example.com/iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4
|
||||||
|
registry.example.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4
|
||||||
|
registry.example.com/iotlcm/iotlcm:iotlcm-app-server-v1.0.2
|
||||||
|
...
|
||||||
|
registry.example.com/iotlcm/iotlcm:mysql-8.0
|
||||||
|
registry.example.com/iotlcm/iotlcm:redis-7.2
|
||||||
|
```
|
||||||
|
|
||||||
|
把清单保存下来,作为这次部署的镜像基线:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose config --images | sort -u > images.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 二、在有网机器导出
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 拉取全部镜像
|
||||||
|
docker compose pull
|
||||||
|
|
||||||
|
# 导出为单个压缩包
|
||||||
|
docker save $(cat images.txt) | gzip > iotlcm-images-$(date +%Y%m%d).tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
单包体积参考:全部 14 个服务约 **1.5~2 GB**(压缩后)。
|
||||||
|
|
||||||
|
若目标机磁盘紧张,可按服务分包:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for img in $(cat images.txt); do
|
||||||
|
name=$(echo "$img" | tr '/:' '__')
|
||||||
|
docker save "$img" | gzip > "img-${name}.tar.gz"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、传输
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp iotlcm-images-*.tar.gz user@target-host:/opt/iotlcm/
|
||||||
|
```
|
||||||
|
|
||||||
|
无网络直连时用移动介质拷入,注意校验完整性:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 源端生成校验值
|
||||||
|
sha256sum iotlcm-images-*.tar.gz > images.sha256
|
||||||
|
|
||||||
|
# 目标端校验
|
||||||
|
sha256sum -c images.sha256
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、在目标机导入
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunzip -c iotlcm-images-*.tar.gz | docker load
|
||||||
|
```
|
||||||
|
|
||||||
|
分包的逐个导入:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for f in img-*.tar.gz; do gunzip -c "$f" | docker load; done
|
||||||
|
```
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker images | grep iotlcm
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、部署
|
||||||
|
|
||||||
|
导入后镜像已在本机,compose 不会再尝试拉取:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 把 docker-compose.yml 里的镜像地址与 TAG 改成与导入的镜像一致
|
||||||
|
cp -r config.example config
|
||||||
|
vi config/*/config.yaml
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning REGISTRY 与 TAG 必须与导入的镜像名一致
|
||||||
|
`docker load` 只恢复镜像名,不会改标签。若 `docker-compose.yml` 里写 `TAG=latest` 而导入的是 `1.0.3`,compose 会认为镜像不存在并尝试拉取,离线环境直接失败。
|
||||||
|
|
||||||
|
用这条命令核对:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose config --images | sort -u > expected.txt
|
||||||
|
docker images --format '{{.Repository}}:{{.Tag}}' | sort -u > actual.txt
|
||||||
|
comm -23 expected.txt actual.txt # 输出为空才说明镜像齐全
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 六、长期方案:内网镜像仓库
|
||||||
|
|
||||||
|
如果需要经常部署或多机部署,建议搭内网镜像仓库,而不是每次传 tar 包。
|
||||||
|
|
||||||
|
### 同步镜像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在能访问外网的机器
|
||||||
|
for img in $(cat images.txt); do
|
||||||
|
docker pull "$img"
|
||||||
|
newimg="${img/registry.example.com/harbor.internal.example.com}"
|
||||||
|
docker tag "$img" "$newimg"
|
||||||
|
docker push "$newimg"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
然后目标机的 `docker-compose.yml` 改为:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
REGISTRY=harbor.internal.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### 差异更新
|
||||||
|
|
||||||
|
版本升级时只需推送变化的镜像:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
# 对比 images.txt 找出新增/变更的标签,只同步这些
|
||||||
|
```
|
||||||
|
|
||||||
|
## 七、常见问题
|
||||||
|
|
||||||
|
| 现象 | 原因 | 处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `docker compose up` 卡在 `Pulling` | 镜像标签不匹配,compose 尝试联网拉取 | 按上文核对 `expected.txt` 与 `actual.txt` |
|
||||||
|
| `no space left on device` | 镜像包 + 解压后镜像双份占盘 | 导入后删除 tar 包,或换挂载点 |
|
||||||
|
| `load` 后 `docker images` 为空 | 压缩包损坏 | 用 `sha256sum -c` 校验 |
|
||||||
|
| 容器启动报 `exec format error` | 架构不匹配(x86 镜像跑在 ARM) | 在目标架构上重新导出 |
|
||||||
|
| 导出包过大 | 包含多个无用层 | 用 `docker save` 只导出必要标签 |
|
||||||
|
|
||||||
|
## 八、离线环境的其他准备
|
||||||
|
|
||||||
|
镜像只是第一步,以下也需提前准备:
|
||||||
|
|
||||||
|
| 资源 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 部署仓代码 | `git clone` 或直接打包 `iotlcm-deploy/` 目录带过去 |
|
||||||
|
| 配置模板 | 已包含在部署仓 `config/` |
|
||||||
|
| 数据库脚本 | 已包含在部署仓 `schema/` |
|
||||||
|
| Docker 与 Compose | 目标机需预先安装,离线安装包需单独准备 |
|
||||||
|
| 时区数据 | 若使用 `Asia/Shanghai`,确认容器内 tzdata 完整(官方镜像已内置) |
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
---
|
||||||
|
title: 升级与备份
|
||||||
|
description: 版本固定、升级回滚、数据备份与日志清理
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 升级与备份
|
||||||
|
|
||||||
|
## 版本固定
|
||||||
|
|
||||||
|
生产环境**必须固定镜像标签**,不要用 `latest`:
|
||||||
|
|
||||||
|
- `latest` 会随仓库推送变化,同一份编排在不同时间拉到的镜像不同
|
||||||
|
- 出问题无法确定运行版本
|
||||||
|
- 回滚时没有明确目标
|
||||||
|
|
||||||
|
`docker-compose.yml` 中指定:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TAG=1.0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
记录当前部署版本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M') 镜像=$(grep -m1 'iotlcm-admin-svr:' docker-compose.yml)" >> deploy-history.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## 升级
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 备份(不要跳过,见下节)
|
||||||
|
./backup.sh
|
||||||
|
|
||||||
|
# 2. 拉新镜像
|
||||||
|
vi docker-compose.yml # 修改镜像 TAG
|
||||||
|
docker compose pull
|
||||||
|
|
||||||
|
# 3. 更新
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 4. 验证
|
||||||
|
docker compose ps
|
||||||
|
docker compose logs --tail=50 admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
滚动更新单个服务减少中断:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --no-deps admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回滚
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vi docker-compose.yml # TAG 改回旧版本
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning 数据库变更不能随代码回滚
|
||||||
|
若新版本含数据库结构变更,回滚代码后数据库仍是新结构。升级前确认变更可逆,或保留升级前的完整备份。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 数据库重建
|
||||||
|
|
||||||
|
`schema/` 脚本只在 `data/mysql` 为空时执行:
|
||||||
|
|
||||||
|
| 情况 | 是否需要重建 |
|
||||||
|
| --- | --- |
|
||||||
|
| 升级应用镜像 | 否 |
|
||||||
|
| 修改 `config/` | 否,重启对应服务即可 |
|
||||||
|
| 修改 `schema/` 脚本 | 是(会丢数据) |
|
||||||
|
| 首次初始化失败 | 是 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
sudo rm -rf ./data/mysql
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
::: danger 生产环境禁止清空 data/mysql
|
||||||
|
生产数据必须通过数据库工具单独备份与迁移,不能依赖 schema 重建。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 备份
|
||||||
|
|
||||||
|
### 备份脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
STAMP=$(date '+%Y%m%d-%H%M%S')
|
||||||
|
DEST="./backups/${STAMP}"
|
||||||
|
mkdir -p "$DEST"
|
||||||
|
|
||||||
|
echo "[1/3] 导出数据库..."
|
||||||
|
docker compose exec -T mysql sh -c \
|
||||||
|
'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --events db_iotlcm' \
|
||||||
|
| gzip > "${DEST}/db_iotlcm.sql.gz"
|
||||||
|
|
||||||
|
echo "[2/3] 备份配置..."
|
||||||
|
tar czf "${DEST}/config.tar.gz" config/ docker-compose.yml
|
||||||
|
|
||||||
|
echo "[3/3] 备份上传文件..."
|
||||||
|
tar czf "${DEST}/upload.tar.gz" data/upload/ 2>/dev/null || echo " 上传目录为空,跳过"
|
||||||
|
|
||||||
|
echo "完成:${DEST}"
|
||||||
|
ls -lh "$DEST"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x backup.sh && ./backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 只备份数据库
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec -T mysql sh -c \
|
||||||
|
'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction db_iotlcm' \
|
||||||
|
| gzip > "db_iotlcm-$(date +%Y%m%d).sql.gz"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 恢复
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gunzip -c db_iotlcm-20260101.sql.gz | \
|
||||||
|
docker compose exec -T mysql sh -c \
|
||||||
|
'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 备份内容与优先级
|
||||||
|
|
||||||
|
| 内容 | 位置 | 重要性 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 数据库 | MySQL 容器内 `db_iotlcm` | **最高**,业务数据全部在此 |
|
||||||
|
| 配置 | `config/` + `docker-compose.yml` | 高,丢失需重新配置 |
|
||||||
|
| 上传文件 | `data/upload/` | 中,附件与图片 |
|
||||||
|
| Redis 数据 | `data/redis/` | 低,缓存可重建 |
|
||||||
|
| RabbitMQ 队列 | `data/rabbitmq/` | 低,未消费消息会丢失 |
|
||||||
|
| 日志 | `data/logs/` | 低,排障用 |
|
||||||
|
|
||||||
|
### 备份策略
|
||||||
|
|
||||||
|
| 场景 | 频率 | 保留 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 生产 | 每日一次 + 升级前 | 30 天 |
|
||||||
|
| 试用 | 有数据变更时 | 7 天 |
|
||||||
|
|
||||||
|
备份文件应转存到异机或对象存储,不要与数据库同盘。
|
||||||
|
|
||||||
|
## 日志清理
|
||||||
|
|
||||||
|
日志写在 `data/logs/` 下按服务分目录,应用侧已按小时轮转(`{Y-m-d-H}.log`)。清理旧日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 删除 7 天前的日志
|
||||||
|
find ./data/logs -name '*.log' -mtime +7 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
建议配置 cron 定期执行:
|
||||||
|
|
||||||
|
```cron
|
||||||
|
0 3 * * * root find /path/to/iotlcm-deploy/data/logs -name '*.log' -mtime +7 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
## 监控
|
||||||
|
|
||||||
|
基础组件自带端点:
|
||||||
|
|
||||||
|
| 服务 | 端点 |
|
||||||
|
| --- | --- |
|
||||||
|
| RabbitMQ | `http://<host>:15672` 管理界面 |
|
||||||
|
| Consul | `http://<host>:8500/ui` |
|
||||||
|
| MySQL | `docker compose exec mysql mysqladmin status` |
|
||||||
|
|
||||||
|
建议告警指标:
|
||||||
|
|
||||||
|
- MySQL 连接数与磁盘使用率
|
||||||
|
- Redis 内存使用率
|
||||||
|
- RabbitMQ 队列积压(`iotlcm.batch.execute`、`iotlcm.app.business`)
|
||||||
|
- worker-svr 全量同步任务失败数
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
title: 可选服务
|
||||||
|
description: 运营商接口挡板、飞书通知与反向代理
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 可选服务
|
||||||
|
|
||||||
|
以下服务默认不启动,按需通过 profile 开启。
|
||||||
|
|
||||||
|
## 运营商接口挡板
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
模拟中国移动 OneLink 接口。**没有真实运营商环境时,靠它跑通完整链路**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
admin-svr → worker-svr → upstream-chan → onelink-mock
|
||||||
|
```
|
||||||
|
|
||||||
|
不开挡板时,`upstream-chan` 会尝试连接真实运营商接口。没有凭据则批次执行、状态同步、流量查询全部失败。
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api.json
|
||||||
|
```
|
||||||
|
|
||||||
|
预期 `200`。接口文档在 `http://127.0.0.1:8080/swagger`。
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|
挡板自身无需配置,数据存在容器内 SQLite(`./data/mock/onelink_mock.db`)。
|
||||||
|
|
||||||
|
`upstream-chan` 配置中已预置指向:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
sim_yd_mock_v5:
|
||||||
|
url: "http://onelink-mock:8080/v5/"
|
||||||
|
```
|
||||||
|
|
||||||
|
数据库种子脚本 `schema/08-seed-mock-channel.sql` 已插入 Mock 通道(`channel_yd_mock`)并指向该地址。
|
||||||
|
|
||||||
|
`app-server` 若使用挡板,把 `upstream.onelinkMock.enabled` 改为 `true`,`baseUrl` 填 `http://onelink-mock:8080/v5`。
|
||||||
|
|
||||||
|
### 造测试卡
|
||||||
|
|
||||||
|
挡板支持生成模拟卡片,用于验证查询、开停复机、续费流程。接口见挡板 Swagger 页面。
|
||||||
|
|
||||||
|
### 切换到真实运营商
|
||||||
|
|
||||||
|
1. 删除或注释 `upstream-chan` 配置中的 `sim_yd_mock_v5` 段
|
||||||
|
2. 在平台「通道管理」录入真实运营商通道(AppKey、AppSecret、接口地址)
|
||||||
|
3. 停用 Mock 通道,避免卡片误挂
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock stop onelink-mock
|
||||||
|
```
|
||||||
|
|
||||||
|
## 飞书通知
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
把全量同步完成、失败告警等运维事件推送到飞书群。
|
||||||
|
|
||||||
|
### 准备
|
||||||
|
|
||||||
|
在飞书开放平台创建自建应用,获取 `appId`、`appSecret` 与目标群的 `chatId`,填入 `docker-compose.yml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FEISHU_APP_ID=cli_xxxxxxxx
|
||||||
|
FEISHU_APP_SECRET=xxxxxxxx
|
||||||
|
FEISHU_CHAT_ID=oc_xxxxxxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-bot up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/full-sync`),该目录有数据后才会产生通知。
|
||||||
|
|
||||||
|
## 反向代理
|
||||||
|
|
||||||
|
### 用途
|
||||||
|
|
||||||
|
把分散在各端口的服务统一到域名下按路径分流。宿主机已有 nginx 或面板时不需要。
|
||||||
|
|
||||||
|
### 域名与分流
|
||||||
|
|
||||||
|
编辑 `config/nginx/iotlcm.conf`:
|
||||||
|
|
||||||
|
| 域名 | 分流到 |
|
||||||
|
| --- | --- |
|
||||||
|
| `api.your-domain.example.com` | openapi(`/openapi/v3/`、`/route/open/`) |
|
||||||
|
| `admin.your-domain.example.com` | 管理端前端 + `/docs/` 文档站 |
|
||||||
|
| `app.your-domain.example.com` | 小程序前端 + `/app/api/v1/` 后端 |
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-nginx up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
默认监听 80,改 `docker-compose.yml` 的 `NGINX_HTTP_PORT` 可调整。
|
||||||
|
|
||||||
|
### 使用宿主机 nginx
|
||||||
|
|
||||||
|
不启动容器时,把 `config/nginx/iotlcm.conf` 中的 `upstream` 与 `location` 段落复制到宿主机 nginx 配置。
|
||||||
|
|
||||||
|
::: warning 宿主机 nginx 无法解析容器名
|
||||||
|
需要把 `upstream` 里的服务名换成宿主机地址:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 容器内写法
|
||||||
|
server admin-svr:8008;
|
||||||
|
# 宿主机写法
|
||||||
|
server 127.0.0.1:8008;
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 启用 HTTPS
|
||||||
|
|
||||||
|
自带配置未包含证书,启用需三步:
|
||||||
|
|
||||||
|
1. 准备证书,放入 `data/certs/`
|
||||||
|
2. 在 server 块加入 SSL 配置:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
listen 443 ssl;
|
||||||
|
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 给 nginx 服务增加证书目录挂载,并加 80 → 443 跳转:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./data/certs:/etc/nginx/certs:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name admin.your-domain.example.com;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 组合启动
|
||||||
|
|
||||||
|
多个 profile 可同时启用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock --profile with-nginx up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
查看指定 profile 下的服务清单:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock config --services
|
||||||
|
```
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
---
|
||||||
|
title: 环境准备
|
||||||
|
description: 部署 IotLCM 所需的硬件、系统、Docker 与网络条件
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 硬件要求
|
||||||
|
|
||||||
|
按数据规模选择:
|
||||||
|
|
||||||
|
| 场景 | CPU | 内存 | 磁盘 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 试用 / 演示 | 2 核 | 4 GB | 40 GB | 少量卡片,建议停用定时同步任务 |
|
||||||
|
| 生产(中小规模) | 4 核 | 8 GB | 200 GB | 万级卡片,开启全量同步 |
|
||||||
|
| 生产(大规模) | 8 核+ | 16 GB+ | 500 GB+ | 十万级卡片,建议数据库独立部署 |
|
||||||
|
|
||||||
|
磁盘估算:MySQL 数据约 1~2 KB/张卡;日志随同步任务量增长,建议单独挂载并配置轮转。
|
||||||
|
|
||||||
|
生产 8 GB 场景的内存分配参考:
|
||||||
|
|
||||||
|
| 服务 | 占用 |
|
||||||
|
| --- | --- |
|
||||||
|
| MySQL | 1~2 GB |
|
||||||
|
| Redis | 200~500 MB |
|
||||||
|
| RabbitMQ | 300~500 MB |
|
||||||
|
| worker-svr | 500 MB~1 GB(与并发同步卡片数相关) |
|
||||||
|
| 其余业务服务 | 各 100~200 MB |
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
|
||||||
|
| 项 | 要求 |
|
||||||
|
| --- | --- |
|
||||||
|
| 操作系统 | Linux x86_64(CentOS 7+ / Ubuntu 20.04+ / Debian 11+) |
|
||||||
|
| 内核 | 3.10 以上 |
|
||||||
|
| 文件系统 | ext4 / xfs |
|
||||||
|
| 时区 | 建议 `Asia/Shanghai`,与 `docker-compose.yml` 的 `TZ` 保持一致 |
|
||||||
|
|
||||||
|
## Docker 要求
|
||||||
|
|
||||||
|
| 组件 | 最低版本 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Docker Engine | 20.10 | 需支持 `healthcheck` 与 `profiles` |
|
||||||
|
| Docker Compose | v2.0(插件版) | 使用 `docker compose` 而非 `docker-compose` |
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker version
|
||||||
|
docker compose version
|
||||||
|
```
|
||||||
|
|
||||||
|
`docker compose version` 需 ≥ v2.0。若只有 `docker-compose`(v1),请升级到 Compose V2 插件。
|
||||||
|
|
||||||
|
## 镜像仓库
|
||||||
|
|
||||||
|
平台镜像发布在阿里云容器镜像服务,**仓库类型为公开,无需登录即可拉取**。
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 公网地址 | `crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com` |
|
||||||
|
| VPC 地址 | `crpi-qflrqrxoohlkkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com` |
|
||||||
|
| 命名空间 | `iotlcm` |
|
||||||
|
| 地域 | 西南1(成都) |
|
||||||
|
|
||||||
|
**所有镜像都放在同一个镜像仓库** `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-openapi-v1.0.4
|
||||||
|
crpi-.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
tag 格式统一为 **`<服务名>-<版本>`**。
|
||||||
|
|
||||||
|
地址结构是 `<Registry>/<命名空间>/<镜像仓库>:<tag>`,即:
|
||||||
|
|
||||||
|
```text
|
||||||
|
crpi-...cn-chengdu.personal.cr.aliyuncs.com / iotlcm / iotlcm : iotlcm-admin-svr-v1.0.4
|
||||||
|
↑ 命名空间 ↑ 镜像仓库 ↑ tag
|
||||||
|
```
|
||||||
|
|
||||||
|
`docker-compose.yml` 里的镜像地址已预置为上述公网地址,一般无需修改。
|
||||||
|
|
||||||
|
::: tip 基础组件也在同一仓库
|
||||||
|
MySQL、Redis、RabbitMQ、Consul、nginx 同样在这个仓库里(`mysql-8.0`、`redis-7.2` 等 tag),不从 Docker Hub 直连(公网不稳定且 tag 会漂)。版本已在编排文件中固定。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: tip 阿里云 VPC 地址
|
||||||
|
若部署在阿里云同地域 ECS 上,可把编排文件里的 Registry 前缀改为 VPC 地址,走内网流量、不消耗公网带宽:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: 'crpi-qflrqrxoohlkkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:mysql-8.0'
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 网络与防火墙
|
||||||
|
|
||||||
|
仅需对外开放反向代理端口(默认 80 / 443),其余服务端口应限制为内网访问。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 示例:firewalld 仅放行 80/443
|
||||||
|
firewall-cmd --permanent --add-service=http
|
||||||
|
firewall-cmd --permanent --add-service=https
|
||||||
|
firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 默认只监听本机
|
||||||
|
`admin-svr`、`admin-ui`、`openapi`、`app-server`、`app-web`、`docs` 默认绑定 `127.0.0.1`,只能本机访问。对外需通过反向代理,见[可选服务](optional)。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 外部依赖(可选)
|
||||||
|
|
||||||
|
以下能力需要外部账号或服务,**不影响平台主流程**:
|
||||||
|
|
||||||
|
| 服务 | 用途 | 不配置的影响 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 短信服务商 | 发送验证码 | 无法发送短信 |
|
||||||
|
| 微信支付 | 在线支付 | 支付功能不可用 |
|
||||||
|
| 飞书 | 运维通知 | 不启动 bot 即可 |
|
||||||
|
|
||||||
|
::: tip 时序库已内置
|
||||||
|
流量历史使用的 TDengine **已包含在编排里**,由 `tdengine-init` 服务在首次启动时自动建库建表,无需额外申请或配置:
|
||||||
|
|
||||||
|
| 库 | 分片 | 保留 | 超级表 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `iotlcm_daily` | 7 天 | 3 年 | `t_sim_card_usage_daily` |
|
||||||
|
| `iotlcm_monthly` | 30 天 | 3 年 | `t_sim_card_usage_monthly` |
|
||||||
|
|
||||||
|
编排使用的版本是 `3.3.6.13`(公开镜像可得),生产为 `3.4.0.9`。如需与生产完全一致,可自行构建镜像并替换 `docker-compose.yml` 中的 tag。
|
||||||
|
:::
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
---
|
||||||
|
title: 分离部署
|
||||||
|
description: 中间件独立部署,业务服务单独一台或多台
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 分离部署
|
||||||
|
|
||||||
|
把 MySQL、Redis、RabbitMQ、Consul 放到独立主机或使用云托管,业务服务单独部署。
|
||||||
|
|
||||||
|
## 适用场景
|
||||||
|
|
||||||
|
- 数据库已有云托管实例(RDS、云 Redis、云 MQ)
|
||||||
|
- 中间件已有独立集群,不希望在业务机上再跑一套
|
||||||
|
- 业务机配置较低,不适合同时承载数据库
|
||||||
|
|
||||||
|
## 部署结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌─────────────────────┐ ┌──────────────────────┐
|
||||||
|
│ 中间件主机 / 云服务 │ │ 业务主机 │
|
||||||
|
│ │ │ │
|
||||||
|
│ MySQL :3306 │◄───────┤ admin-svr :8008 │
|
||||||
|
│ Redis :6379 │ │ admin-ui :80 │
|
||||||
|
│ RabbitMQ :5672 │ │ openapi :8000 │
|
||||||
|
│ Consul :8500 │ │ app-server :8010 │
|
||||||
|
│ │ │ worker-svr │
|
||||||
|
└─────────────────────┘ │ upstream-chan/-proxy│
|
||||||
|
└──────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 一、准备中间件
|
||||||
|
|
||||||
|
在中间件侧完成(云托管通常已就绪):
|
||||||
|
|
||||||
|
| 组件 | 需准备 |
|
||||||
|
| --- | --- |
|
||||||
|
| MySQL | 建库 `db_iotlcm`、执行 `schema/` 脚本、建应用账号 `u_iotlcm` |
|
||||||
|
| Redis | 开启认证,记录地址与口令 |
|
||||||
|
| RabbitMQ | 建用户与 vhost,记录地址与口令 |
|
||||||
|
| Consul | 单机模式即可,记录地址 |
|
||||||
|
|
||||||
|
### 初始化数据库
|
||||||
|
|
||||||
|
在能连到目标 MySQL 的机器上执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 按文件名顺序执行全部脚本
|
||||||
|
for f in schema/0*.sql; do
|
||||||
|
echo "执行 $f"
|
||||||
|
mysql -h <MySQL地址> -uroot -p<口令> < "$f"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning `.sh` 脚本需单独执行
|
||||||
|
`schema/04-users.sh` 与 `schema/05-reset-passwords.sh` 依赖环境变量,不能直接重定向执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MYSQL_ROOT_PASSWORD='<口令>' MYSQL_APP_PASSWORD='<应用口令>' \
|
||||||
|
MYSQL_HOST=<地址> bash schema/04-users.sh
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 二、准备业务机 Compose
|
||||||
|
|
||||||
|
复制 `docker-compose.yml`,删除四个中间件服务(`mysql`、`redis`、`rabbitmq`、`consul`),并移除各处 `depends_on` 对它们的引用。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.app.yml
|
||||||
|
services:
|
||||||
|
admin-svr:
|
||||||
|
image: '${REGISTRY}/iotlcm/iotlcm:iotlcm-admin-svr-${TAG}'
|
||||||
|
# ... 保留 ports / volumes / environment
|
||||||
|
# 删除 depends_on 中对 mysql/redis/rabbitmq 的依赖
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 网络要求
|
||||||
|
业务机必须能访问中间件主机的对应端口。若有防火墙,放行:
|
||||||
|
|
||||||
|
- 业务机 → 中间件:3306、6379、5672、8500
|
||||||
|
- 外部 → 业务机:仅反向代理端口(80 / 443)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 三、修改配置指向外部地址
|
||||||
|
|
||||||
|
这是本节最关键的一步。所有配置里以**服务名**书写的地址都要改成实际地址。
|
||||||
|
|
||||||
|
### admin-svr
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
database:
|
||||||
|
default:
|
||||||
|
link: "mysql:u_iotlcm:<口令>@tcp(<MySQL地址>:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
default:
|
||||||
|
address: "<Redis地址>:6379"
|
||||||
|
pass: "<Redis口令>"
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
dsn: "amqp://<用户>:<口令>@<RabbitMQ地址>:5672/"
|
||||||
|
```
|
||||||
|
|
||||||
|
### openapi
|
||||||
|
|
||||||
|
同上三处:`database.default.link`、`redis.default.address`、`rabbitmq.dsn`。
|
||||||
|
|
||||||
|
### worker-svr
|
||||||
|
|
||||||
|
同上三处。注意 `tsdb.tdengine.dsn` 若使用 TDengine 也要改为实际地址。
|
||||||
|
|
||||||
|
### app-server
|
||||||
|
|
||||||
|
改 `database.default.link` 与 `redis.default`。
|
||||||
|
|
||||||
|
### upstream-chan / upstream-proxy
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
registry:
|
||||||
|
type: "consul"
|
||||||
|
address: "<Consul地址>:8500"
|
||||||
|
```
|
||||||
|
|
||||||
|
同时改 `database.default.link` 与 `redis.default`。
|
||||||
|
|
||||||
|
### 服务互访地址
|
||||||
|
|
||||||
|
若业务服务分散在多台机器,`admin-svr`、`worker-svr`、`app-server` 配置里的上游地址也要改:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
upstream:
|
||||||
|
proxy:
|
||||||
|
address: "<upstream-proxy所在主机>:8510"
|
||||||
|
gateway:
|
||||||
|
address: "<upstream-chan所在主机>:8110"
|
||||||
|
```
|
||||||
|
|
||||||
|
`upstream-proxy` 的 `gateway.address` 同理。
|
||||||
|
|
||||||
|
::: danger 最容易出的错
|
||||||
|
只改数据库地址,忘了改 `upstream-proxy:8510`、`upstream-chan:8110` 这类服务名。症状是服务能启动、数据库也通,但批次执行、智能诊断全部超时。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 四、启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 按需修改 docker-compose.app.yml 的镜像地址、端口与口令
|
||||||
|
cp -r config.example config
|
||||||
|
vi config/*/config.yaml # 按上节改为外部地址
|
||||||
|
|
||||||
|
docker compose -f docker-compose.app.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
此时 `docker-compose.yml` 中的 `MYSQL_*`、`REDIS_PASSWORD`、`RABBITMQ_PASSWORD` 不再用于启动中间件容器,但仍作为配置中的变量来源,**取值要与中间件实际口令一致**。
|
||||||
|
|
||||||
|
## 五、验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.app.yml ps
|
||||||
|
|
||||||
|
# 数据库连通性
|
||||||
|
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'nc -zv <MySQL地址> 3306'
|
||||||
|
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'nc -zv <Redis地址> 6379'
|
||||||
|
|
||||||
|
# Redis 认证
|
||||||
|
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'redis-cli -h <地址> -a "<口令>" ping'
|
||||||
|
|
||||||
|
# 服务日志
|
||||||
|
docker compose -f docker-compose.app.yml logs --tail=50 admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
浏览器登录管理端,确认能查卡、能发起业务办理。
|
||||||
|
|
||||||
|
## 六、注意事项
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 网络延迟 | 业务机到中间件的延迟建议 < 5ms。跨地域部署会造成批次执行明显变慢 |
|
||||||
|
| 连接池 | 外部数据库延迟较高时,适当调大 `database.default.maxIdle` 与 `maxOpen` |
|
||||||
|
| 备份 | 分离后数据库备份由中间件侧负责,本仓的 `backup.sh` 需调整连接参数 |
|
||||||
|
| 时钟 | 各机器需开启 NTP 同步,否则全量同步的时间窗口判断会出错 |
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
---
|
||||||
|
title: 故障排查
|
||||||
|
description: 部署与运行中的常见问题及处理
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 故障排查
|
||||||
|
|
||||||
|
## 通用排查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps # 容器状态
|
||||||
|
docker compose logs --tail=100 <服务> # 最近日志
|
||||||
|
docker compose logs -f <服务> # 跟随日志
|
||||||
|
docker compose exec <服务> sh # 进容器
|
||||||
|
```
|
||||||
|
|
||||||
|
## 启动类
|
||||||
|
|
||||||
|
### MySQL 一直不 healthy
|
||||||
|
|
||||||
|
**现象**:`iotlcm-mysql` 反复重启或一直 `starting`,其余服务不启动。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs mysql | tail -50
|
||||||
|
```
|
||||||
|
|
||||||
|
| 日志关键词 | 原因 | 处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Access denied` | root 口令与旧数据卷不一致 | 改 `docker-compose.yml` 与卷内一致,或清空 `data/mysql` 重建 |
|
||||||
|
| `Can't create/write to file` | 目录权限 | `sudo chown -R 999:999 data/mysql` |
|
||||||
|
| `No space left on device` | 磁盘满 | 清理磁盘 |
|
||||||
|
| `Can't open shared library` | 架构不匹配 | 确认镜像与宿主机架构一致 |
|
||||||
|
|
||||||
|
### schema 初始化失败
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs mysql | grep -iE 'error' | head -30
|
||||||
|
```
|
||||||
|
|
||||||
|
| 原因 | 处理 |
|
||||||
|
| --- | --- |
|
||||||
|
| `MYSQL_APP_PASSWORD` 未设置,`04-users.sh` 退出 | 补上后清空 `data/mysql` 重建 |
|
||||||
|
| `MYSQL_ADMIN_PASSWORD` 未设置,`05-reset-passwords.sh` 退出 | 同上 |
|
||||||
|
| SQL 脚本语法问题 | 单独执行该脚本定位 |
|
||||||
|
|
||||||
|
::: warning 改脚本后必须清空数据卷
|
||||||
|
MySQL 只在 `data/mysql` 为空时执行 `schema/`,文件改了不会自动重跑:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down && sudo rm -rf ./data/mysql && docker compose up -d
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 业务服务反复重启
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs --tail=80 admin-svr
|
||||||
|
```
|
||||||
|
|
||||||
|
| 报错 | 原因 | 处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `possible config files "config" not found` | `config/` 不存在或挂载失败 | `cp -r config.example config`,确认 `config/admin-svr/config.yaml` 存在 |
|
||||||
|
| `dial tcp mysql:3306: connection refused` | MySQL 未就绪 | 等 MySQL healthy |
|
||||||
|
| `Access denied for user 'u_iotlcm'` | 应用账号口令与库内不一致 | 确认 `docker-compose.yml` 的 `MYSQL_APP_PASSWORD` 与初始化时一致 |
|
||||||
|
| `panic: unsupported provider xxx` | 通道模板与镜像版本不匹配 | 确认镜像版本与 schema 版本配套 |
|
||||||
|
|
||||||
|
## 功能类
|
||||||
|
|
||||||
|
### 登录后提示未授权 / 接口 401
|
||||||
|
|
||||||
|
**原因**:`JWT_SIGNING_KEY` 在服务间不一致。
|
||||||
|
|
||||||
|
确认 `admin-svr`、`openapi`、`app-server` 三份配置的 `jwt.signingKey` 都引用 `${JWT_SIGNING_KEY}` 且 `docker-compose.yml` 有值,然后重启:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose restart admin-svr openapi app-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### 登录成功但页面空白 / 菜单不显示
|
||||||
|
|
||||||
|
1. 浏览器 F12 看 Network 有无 4xx/5xx
|
||||||
|
2. 检查菜单数据:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT COUNT(*) FROM sys_menu WHERE status=0;"'
|
||||||
|
```
|
||||||
|
|
||||||
|
菜单为 0 说明种子数据未导入,需重建库。
|
||||||
|
|
||||||
|
3. 检查角色菜单关联:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT COUNT(*) FROM sys_role_menu;"'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 卡信息页通道列空白 / 筛选无选项
|
||||||
|
|
||||||
|
**原因**:Mock 通道未插入。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT channel_id, channel_name FROM t_sim_channels;"'
|
||||||
|
```
|
||||||
|
|
||||||
|
为空则手工执行种子脚本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec -T mysql sh -c \
|
||||||
|
'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm' < schema/08-seed-mock-channel.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 批次执行一直失败
|
||||||
|
|
||||||
|
按顺序排查:
|
||||||
|
|
||||||
|
1. **挡板是否启动**(无真实运营商时)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps | grep onelink-mock
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api.json
|
||||||
|
```
|
||||||
|
|
||||||
|
未启动则 `docker compose --profile with-mock up -d`。
|
||||||
|
|
||||||
|
2. **worker-svr 是否连上 RabbitMQ**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs worker-svr | grep -i 'rabbit\|amqp' | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **upstream-chan 是否能连到上游**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs upstream-chan --tail=50
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **是否被运营商限流**
|
||||||
|
|
||||||
|
检查 worker-svr 日志中的 `rate limit`、`429`、`12021`。核对 `batch.inbound.upstreamRateLimit` 的 QPS 是否超过上游配额。
|
||||||
|
|
||||||
|
### 历史流量查询无数据
|
||||||
|
|
||||||
|
**原因**:TDengine 未启动、未完成建表,或配置里的地址不对。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs worker-svr | grep -i 'tdengine\|taos' | tail -20
|
||||||
|
docker compose logs tdengine-init | tail -20 # 建库建表是否成功
|
||||||
|
docker compose exec tdengine taos -s "show databases;"
|
||||||
|
```
|
||||||
|
|
||||||
|
常见原因与处理:
|
||||||
|
|
||||||
|
| 现象 | 原因 | 处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `tdengine-init` 报 MISS | 建表失败 | 看该容器日志里的 `DB error`;改 `tdengine-init/init.sql` 后重跑 `docker compose up -d tdengine-init` |
|
||||||
|
| worker 日志报库表不存在 | 未执行建表 | 同上,确保 init 容器退出码为 0 |
|
||||||
|
| 连接超时 | 地址/端口不对 | 配置里应为 `ws(tdengine:6041)`,容器内用服务名而非 IP |
|
||||||
|
|
||||||
|
建表脚本全部带 `IF NOT EXISTS`,可重复执行。
|
||||||
|
|
||||||
|
### 短信发不出去
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
|
||||||
|
docker compose logs sms --tail=50
|
||||||
|
```
|
||||||
|
|
||||||
|
| 报错 | 原因 |
|
||||||
|
| --- | --- |
|
||||||
|
| `InvalidAccessKeyId` | `docker-compose.yml` 中 AK 未填或错误 |
|
||||||
|
| `isv.SMS_SIGNATURE_ILLEGAL` | `signName` 与服务商审核签名不一致 |
|
||||||
|
| `isv.SMS_TEMPLATE_ILLEGAL` | 模板 ID 未审核通过 |
|
||||||
|
| `缺少 config.templateId` | 场景模板库未配置,调用方需自带 templateId |
|
||||||
|
|
||||||
|
### 图片上传失败 / 不显示
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la data/upload/
|
||||||
|
sudo chown -R 1000:1000 data/upload
|
||||||
|
docker compose restart admin-svr admin-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能类
|
||||||
|
|
||||||
|
### CPU 被 worker-svr 打满
|
||||||
|
|
||||||
|
**原因**:定时任务开启全量同步,大量卡片并发查询上游。
|
||||||
|
|
||||||
|
1. 降低并发与限流:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
batch:
|
||||||
|
inbound:
|
||||||
|
upstreamConcurrency: 50 # 从 300 降低
|
||||||
|
upstreamBatchSize: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 演示 / 试用环境直接停用定时任务:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec mysql sh -c \
|
||||||
|
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "UPDATE sys_job SET status=1;"'
|
||||||
|
```
|
||||||
|
|
||||||
|
种子数据中定时任务默认已全部停用(`status=1`)。
|
||||||
|
|
||||||
|
### 磁盘持续增长
|
||||||
|
|
||||||
|
```bash
|
||||||
|
du -sh data/* | sort -rh
|
||||||
|
```
|
||||||
|
|
||||||
|
| 目录 | 处理 |
|
||||||
|
| --- | --- |
|
||||||
|
| `data/logs` | 清理旧日志,见[升级与备份](operations) |
|
||||||
|
| `data/upload` | 检查异常大的上传文件 |
|
||||||
|
| `data/mysql` | 数据库增长正常,关注 binlog |
|
||||||
|
| `data/redis` | AOF 文件,正常 |
|
||||||
|
|
||||||
|
## 网络类
|
||||||
|
|
||||||
|
### 容器间无法互访
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network inspect iotlcm-network
|
||||||
|
docker compose exec admin-svr sh -c 'nc -zv mysql 3306'
|
||||||
|
```
|
||||||
|
|
||||||
|
配置中应使用 `mysql:3306`、`redis:6379` 这样的服务名,不要写 `127.0.0.1`。
|
||||||
|
|
||||||
|
### 宿主机访问不了 127.0.0.1 绑定的服务
|
||||||
|
|
||||||
|
`admin-svr`、`admin-ui`、`openapi`、`app-server`、`app-web`、`docs` 默认只绑 `127.0.0.1`。对外访问需:
|
||||||
|
|
||||||
|
1. 用 `--profile with-nginx` 启动反向代理,或
|
||||||
|
2. 宿主机 nginx 转发到 `127.0.0.1:<端口>`
|
||||||
|
|
||||||
|
::: danger 不要直接把端口改成 0.0.0.0
|
||||||
|
这会把管理端、数据库等内部服务暴露到公网。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 求助时提供的信息
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose version
|
||||||
|
docker version --format '{{.Server.Version}}'
|
||||||
|
docker compose ps
|
||||||
|
docker compose logs --tail=200 <服务名>
|
||||||
|
df -h && free -h
|
||||||
|
```
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
layout: home
|
||||||
|
|
||||||
|
hero:
|
||||||
|
name: iotLCM
|
||||||
|
text: 文档中心
|
||||||
|
tagline: iotLCM 平台文档与接口规范。
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: 使用手册
|
||||||
|
link: /manual/
|
||||||
|
- theme: alt
|
||||||
|
text: OpenAPI 接口文档
|
||||||
|
link: /openapi/
|
||||||
|
- theme: alt
|
||||||
|
text: 共享 Token 接口文档
|
||||||
|
link: /openapi/operator/
|
||||||
|
|
||||||
|
features:
|
||||||
|
- title: 使用手册
|
||||||
|
details: 按使用端提供 Web 管理平台与小程序端手册,覆盖查卡、诊断、业务管理和账号服务。
|
||||||
|
- title: OpenAPI 接口文档
|
||||||
|
details: 客户开放接口的鉴权方式、请求参数、响应结构、调用示例与兼容接口说明。
|
||||||
|
- title: 共享 Token 接口文档
|
||||||
|
details: 共享 Token 获取与白名单转发说明。
|
||||||
|
- title: 部署手册
|
||||||
|
details: 文档站的推荐环境、Node/Docker 编译方法、nginx 与对象存储部署方式,以及路径前缀配置要点。
|
||||||
|
---
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
layout: home
|
||||||
|
|
||||||
|
hero:
|
||||||
|
name: iotLCM 使用手册
|
||||||
|
text: 操作指南
|
||||||
|
tagline: 按使用端查看卡片查询、业务管理与移动服务说明。
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: Web端手册
|
||||||
|
link: /manual/web/
|
||||||
|
- theme: alt
|
||||||
|
text: 小程序端手册
|
||||||
|
link: /manual/mini-program/
|
||||||
|
|
||||||
|
features:
|
||||||
|
- title: 版本选型指引
|
||||||
|
details: 使用浏览器登录企业管理后台,请阅读 Web 端手册;使用 IOTLCM 物联卡用户服务小程序,请阅读小程序端手册。不同端的功能入口、权限和页面结构有所区别。
|
||||||
|
- title: Web端
|
||||||
|
details: 面向企业管理员和业务人员,覆盖首页、卡信息、智能诊断、业务受理、企业管理、工单、API 凭证与执行日志。
|
||||||
|
- title: 小程序端
|
||||||
|
details: 对应 iotlcm-app v1.0.1,覆盖首页公开查卡、扫码与拍照识别、卡片详情、卡片排查和账号密码登录;办理、绑定、微信登录及注册等功能暂未开放。
|
||||||
|
---
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 登录后办理概览
|
||||||
|
|
||||||
|
登录后的页面会根据账号工作区和权限动态显示。看不到某个入口时,通常是账号未配置对应权限,或该业务仍受版本开关限制。
|
||||||
|
|
||||||
|
## 登录与身份选择
|
||||||
|
|
||||||
|
1. 在首页或需要账号数据的页面进入登录,选择“密码登录”或“验证码登录”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 密码登录支持手机号或用户名;验证码登录需要手机号、图形验证和短信验证码。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 同一登录凭据匹配多个身份时,按页面提示选择本次进入的账号。
|
||||||
|
|
||||||
|
注册、微信登录和“忘记密码”页面当前关闭。无法登录时联系账号管理员或客服。
|
||||||
|
|
||||||
|
## 设备与卡片操作
|
||||||
|
|
||||||
|
登录后点击底部“设备”进入设备列表,或从“办理”进入“我的卡片”。设备页支持关键词搜索、状态筛选、刷新和加载更多;卡片详情显示账号权限范围内的字段及会话、流量信息。首页最多预览三张卡片,并优先展示即将到期的卡;点击“管理”进入全部设备。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; width: 100%; max-width: 440px; margin: 24px auto;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/business-device-tab.png" alt="从底部设备入口查看我的设备" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">底部“设备”入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/business-my-cards-entry.jpg" alt="从办理页面进入我的卡片" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">“办理”中的“我的卡片”入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
续费、变更加油包、停机、复机等卡片更多操作当前暂未开放。重启、数据服务操作和卡片划拨可按页面位置、账号权限及卡片状态开放。页面只显示当前账号可执行的操作,提交前仍会再次校验。
|
||||||
|
|
||||||
|
## 办理入口
|
||||||
|
|
||||||
|
“办理”页包含卡片排查、卡片与客户、工单和执行记录等分组。当前版本按权限开放以下能力:
|
||||||
|
|
||||||
|
| 能力 | 入口与说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 卡片排查 | 输入 ICCID、MSISDN 或 IMSI,查看状态、会话和流量信息 |
|
||||||
|
| 卡片划拨 | 选择启用的直属下级客户和卡片;单次最多100张,提交后到批次日志查看结果 |
|
||||||
|
| 客户管理 | 查看授权范围内的企业客户及客户详情;终端账号不显示此入口 |
|
||||||
|
| 工单服务 | 按列表、提交、详情和处理权限使用相应操作 |
|
||||||
|
| 批次执行日志 | 按列表、概览和明细权限查看批次进度与结果 |
|
||||||
|
|
||||||
|
续费、变更与加油包当前暂未开放;订单、预警和余额充值等页面仍受版本开关限制,进入时可能提示暂未开放。
|
||||||
|
|
||||||
|
## 专题说明
|
||||||
|
|
||||||
|
- [客户管理与卡片划拨](./customers-and-allocation)
|
||||||
|
- [工单服务](./work-orders)
|
||||||
|
- [批次执行日志](./execution-logs)
|
||||||
|
- [消息与个人中心](./messages-and-profile)
|
||||||
|
|
||||||
|
## 操作提示
|
||||||
|
|
||||||
|
- 划拨提交后不要重复创建任务;结果未确认时先核查批次日志和卡片归属。
|
||||||
|
- 工单附件上传失败时检查网络后重试;不要在反馈中提交无关敏感信息。
|
||||||
|
- 所有状态和排查结果仅供业务处理参考,最终以运营商和平台实际结果为准。
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 客户管理与卡片划拨
|
||||||
|
|
||||||
|
本页介绍企业、代理及平台工作区中的客户查询和卡片划拨。入口按账号身份和权限显示;终端账号不提供客户管理入口。
|
||||||
|
|
||||||
|
## 客户管理
|
||||||
|
|
||||||
|
1. 登录后从“我的”或“办理”进入“客户管理”。
|
||||||
|
|
||||||
|
<div class="manual-image-grid" style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; width: 100%; max-width: 440px; margin: 24px auto;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/customer-management-from-profile.png" alt="从我的页面进入客户管理" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">“我的”中的客户管理入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/customer-management-from-business.png" alt="从办理页面进入客户管理" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">“办理”中的客户管理入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. 输入企业名称或手机号搜索客户,也可刷新列表或加载更多。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 点击客户查看详情,包括客户资料、客户类型和实名信息。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 客户详情中的卡片数量和状态统计仅统计该客户直接持有的卡,不包含下级客户。
|
||||||
|
|
||||||
|
列表及详情分别需要对应权限。若搜索无结果,检查名称或手机号;若没有入口,联系账号管理员确认账号类型和授权范围。
|
||||||
|
|
||||||
|
## 单卡划拨
|
||||||
|
|
||||||
|
从卡片详情有权限时显示的“划拨这张卡”入口开始。确认目标客户后检查卡片信息并提交。单卡入口会锁定当前卡,不能改选其他卡。划拨成功后卡片归属转给目标客户。
|
||||||
|
|
||||||
|
## 批量划拨
|
||||||
|
|
||||||
|
1. 从“办理”进入“卡片划拨”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 选择目标客户:仅支持已启用的直属下级客户,可按客户名称或编码搜索。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 搜索并勾选要划拨的卡片。可以选择当前结果;更换搜索条件后,先前选择仍保留。打开已选列表可移除单张或清空选择。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 每次最多选择100张。停机、销户或套餐过期的卡不可划拨。
|
||||||
|
5. 核对目标客户和卡数后提交。后台会整批校验;存在失败卡时整批不执行。
|
||||||
|
6. 提交后进入批次日志查看进度和结果。任务处理中时使用原页面的刷新操作查看状态。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
若网络中断导致结果未知,不要再次新建划拨任务。先通过原批次号查看日志并核对卡片归属;只有确认原任务未受理且没有待执行任务后,才按页面提示核查并重试原任务。仍无法确认时联系管理员处理。
|
||||||
|
|
||||||
|
## 权限与边界
|
||||||
|
|
||||||
|
划拨入口要求企业、代理或平台工作区及卡片划拨权限。目标客户和卡片仍受账号数据范围约束。执行记录列表、概览和明细分别受批次日志权限控制;划拨页面可能提供与本次任务关联的结果查看入口。
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# 卡片详情
|
||||||
|
|
||||||
|
从首页查询结果点击“详情”后进入卡片详情页。首页会以公开来源打开详情,因此未登录或公开来源都会使用受限展示模式。
|
||||||
|
|
||||||
|
## 公开详情展示内容
|
||||||
|
|
||||||
|
公开模式包含两组信息:
|
||||||
|
|
||||||
|
- 基础信息:卡号、卡状态、在线状态;
|
||||||
|
- 流量信息:已用流量。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
卡号可能已经脱敏,页面显示什么就以什么为准。点击或长按卡号行可以复制当前显示值。
|
||||||
|
|
||||||
|
公开模式不会展示完整 IMSI、IMEI、备注、总流量、剩余流量、会话信息、APN、IPv4、IPv6 和服务状态,也不会提供重启、开通数据、停用数据等操作按钮。
|
||||||
|
|
||||||
|
## 登录后详情
|
||||||
|
|
||||||
|
登录并通过权限校验后,设备列表和卡片详情可以展示更完整的账号范围数据,例如 MSISDN、ICCID、IMSI、设备 IMEI、套餐流量和同步时间。字段是否出现取决于接口返回值和账号权限。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/devices-list-account-data-v2.jpg" alt="登录后的设备列表和账号范围数据" style="display: block; width: 100%; height: auto;">
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/devices-search-account-data-v2.jpg" alt="全部设备中的账号范围数据" style="display: block; width: 100%; height: auto;">
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/card-details-account-data-v2.jpg" alt="登录后的卡片详情账号范围数据" style="display: block; width: 100%; height: auto;">
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## 查不到详情时
|
||||||
|
|
||||||
|
- 确认查询号码没有空格或遗漏字符;
|
||||||
|
- 从首页重新查询后再点击“详情”;
|
||||||
|
- 若页面提示当前账号没有权限,请使用公开查询,或联系账号管理员确认数据范围。
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 卡片排查
|
||||||
|
|
||||||
|
卡片排查用于根据卡片状态、在线状态、流量和设备信息整理处理方向。
|
||||||
|
|
||||||
|
## 进入排查
|
||||||
|
|
||||||
|
可以从以下位置进入:
|
||||||
|
|
||||||
|
1. 首页点击“卡片排查”,手动输入卡号;
|
||||||
|
2. 首页查询成功后点击“卡片排查”,系统会带入当前查询值;
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 直接打开卡片排查页,在输入框填写 `ICCID`、`MSISDN` 或 `IMSI`。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/diagnosis-entry-from-home.png" alt="从首页常用入口进入卡片排查" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">首页卡片排查入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/diagnosis-manual-input.jpg" alt="卡片排查页手动输入卡号" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">输入卡号开始排查</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
点击“开始排查”提交。排查过程中不要连续重复点击。
|
||||||
|
|
||||||
|
## 结果摘要
|
||||||
|
|
||||||
|
普通用户或未登录用户通常可以看到:
|
||||||
|
|
||||||
|
- 卡片状态;
|
||||||
|
- 在线状态;
|
||||||
|
- 已用流量或流量状态;
|
||||||
|
|
||||||
|
管理员账号可能看到更多卡片、数据服务、停机冻结和限制策略信息。页面会按实际接口返回值隐藏没有数据的字段。
|
||||||
|
|
||||||
|
## 流量使用分析
|
||||||
|
|
||||||
|
当前页面提供“近1个月、近3个月、近6个月”三个周期。**`v1.0.1` 默认只对顶层管理员显示流量趋势模块;其他账号即使登录,也不保证看到该模块。**
|
||||||
|
|
||||||
|
有数据时会显示周期总用量、日均或月均用量、单期峰值和趋势图;没有数据时显示“暂无历史流量数据”。趋势只用于辅助排查,不等同于实时计费结果。
|
||||||
|
|
||||||
|
## 排查结果的处理方向
|
||||||
|
|
||||||
|
| 结果 | 建议核对 |
|
||||||
|
| --- | --- |
|
||||||
|
| 在线 | 继续核对设备业务表现和应用侧请求 |
|
||||||
|
| 离线 | 检查设备供电、开机状态、信号、天线和 APN |
|
||||||
|
| 未查询到会话 | 稍后重试,并确认设备是否产生数据会话;不等同于卡已销户 |
|
||||||
|
| 流量不足或用量异常 | 核对套餐流量、设备上报和业务使用情况 |
|
||||||
|
| 机卡分离 | 核对卡片与设备的使用关系 |
|
||||||
|
| 区域受限 | 核对业务限制区域、订购状态和触发状态 |
|
||||||
|
| 停机或冻结 | 根据页面提示联系业务管理员确认 |
|
||||||
|
|
||||||
|
## 当前版本限制
|
||||||
|
|
||||||
|
排查页的“重启卡”“开通数据服务”“停用数据服务”按钮只有在登录且账号拥有对应权限时才会出现。业务办理路由、套餐、预警、订单和工单在 `v1.0.1` 中暂未开放。
|
||||||
|
|
||||||
|
排查接口或流量接口提示请求过于频繁时,请等待一段时间后再试,不要连续提交。
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# 批次执行日志
|
||||||
|
|
||||||
|
批量划拨等任务提交后,可通过“我的”或“办理”中的“批次执行日志”查看执行记录。入口、批次概览和明细分别受权限控制。
|
||||||
|
|
||||||
|
## 查询批次
|
||||||
|
|
||||||
|
1. 按批次编号搜索,或展开筛选条件。
|
||||||
|
2. 可按渠道、操作模式、录入方式和执行状态组合筛选;点击刷新重新查询,清除筛选可恢复完整列表。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 点击记录查看批次概览和执行明细;需要时刷新详情,或切换为仅查看失败项。
|
||||||
|
4. 列表支持加载更多。划拨任务可能从划拨页面直接打开关联批次详情。
|
||||||
|
|
||||||
|
<div class="manual-image-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/execution-log-load-more.png" alt="批次执行日志列表中的加载更多入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">加载更多执行记录</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/execution-log-allocation-entry.png" alt="卡片划拨批次的查看详情入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">卡片划拨详情入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/execution-log-allocation-details.png" alt="划拨任务概览和逐卡执行明细" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">划拨任务概览与卡片明细</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
记录会显示批次号、提交渠道、操作类型、录入方式和执行状态。明细权限不足时仍可能看到列表或概览,但无法查看逐卡结果;具体字段按接口权限和返回数据展示。
|
||||||
|
|
||||||
|
## 状态未确认
|
||||||
|
|
||||||
|
任务处于待处理或处理中时,刷新原批次查看进度。若提交后网络中断且页面提示结果未确认,不要新建相同任务;保留原批次号,先查看日志并核对卡片归属。只有确认任务未受理且不存在待执行任务后,才能按页面提示重试原任务。
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# 常见问题
|
||||||
|
|
||||||
|
## v1.0.1 有哪些可用功能?
|
||||||
|
|
||||||
|
无需登录即可在首页手动输入、扫码或拍照识别卡号,并查看公开详情和卡片排查结果。已有账号可以使用账号密码登录,查看权限范围内的设备数据。
|
||||||
|
|
||||||
|
## 为什么底部没有“办理”Tab?
|
||||||
|
|
||||||
|
`v1.0.1` 关闭了办理 Tab。套餐续费、加油包、套餐变更、停复机、预警、订单、工单和客户管理等路由也暂未开放,直接访问会提示当前版本暂未开放此功能。
|
||||||
|
|
||||||
|
## 为什么设备页看不到卡片?
|
||||||
|
|
||||||
|
设备页读取登录账号的卡片范围。未登录时请回到首页使用公开查卡;登录后仍为空时,请确认账号已分配卡片或具备相应权限。
|
||||||
|
|
||||||
|
## 支持哪些查卡方式?
|
||||||
|
|
||||||
|
首页支持输入 `ICCID`、`MSISDN`、`IMSI` 或 `IMEI`,也支持扫码读取内容和从相机或相册拍照识别 ICCID。
|
||||||
|
|
||||||
|
## 拍照识别失败怎么办?
|
||||||
|
|
||||||
|
请拍摄单张、平整、完整、无反光的卡片,并确保 ICCID 清晰。识别超时或频繁请求时等待后重试;也可以改用扫码或手动输入。
|
||||||
|
|
||||||
|
## 公开详情为什么字段少?
|
||||||
|
|
||||||
|
公开模式只展示卡号、卡状态、在线状态和已用流量。完整 IMSI、IMEI、会话、APN、IP、服务状态等字段属于受限数据。
|
||||||
|
|
||||||
|
## 为什么不能绑定卡片或执行重启?
|
||||||
|
|
||||||
|
卡片绑定和“更多操作”在 `v1.0.1` 中统一关闭。即使登录,当前版本也不会提供绑定、套餐办理、停复机、数据服务变更和预警配置入口。
|
||||||
|
|
||||||
|
## 为什么看不到流量趋势?
|
||||||
|
|
||||||
|
流量趋势模块当前只对顶层管理员开放,且接口必须返回有效历史数据。其他账号或没有历史数据时不会显示趋势图。
|
||||||
|
|
||||||
|
## 为什么排查显示离线或未查询到会话?
|
||||||
|
|
||||||
|
先检查设备供电、开机状态、信号、天线和 APN,再稍后重新排查。“未查询到会话”只表示当前没有查到数据会话,不等同于卡片已销户。
|
||||||
|
|
||||||
|
## 账号密码登录有什么限制?
|
||||||
|
|
||||||
|
当前版本只开放已有账号的账号密码登录;微信登录和注册账号未开放。忘记密码页面会提示短信服务接入后开放,请联系管理员或客服处理账号问题。
|
||||||
|
|
||||||
|
## 排查结果是否是最终结论?
|
||||||
|
|
||||||
|
不是。排查结果仅用于辅助判断,最终以运营商和业务系统状态为准。请勿在查询、排查和反馈内容中提交身份证号、银行卡号或其他无关敏感信息。
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 首页查卡
|
||||||
|
|
||||||
|
首页是当前版本的主要使用入口。查询不要求登录,支持手动输入、微信小程序扫码和图片识别三种录入方式。
|
||||||
|
|
||||||
|
## 登录入口
|
||||||
|
|
||||||
|
首页和登录页都保留“未登录也可查卡”入口。需要查看账号名下设备时,再通过账号密码登录;微信登录和注册账号暂未开放。
|
||||||
|
|
||||||
|
## 手动查询
|
||||||
|
|
||||||
|
1. 在“卡片查询”输入框输入完整的 `ICCID`、`MSISDN`、`IMSI` 或 `IMEI`。
|
||||||
|
2. 点击“查询”,等待查询完成。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 查询成功后,结果区域显示卡号、使用流量和在线状态。
|
||||||
|
4. 点击“详情”查看公开卡片详情,或点击“卡片排查”查看状态摘要。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/query-actions-v2.jpg" alt="查询结果中的详情和卡片排查入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">详情与卡片排查入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/card-details-v2.jpg" alt="卡片详情页展示的公开字段" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">卡片详情</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/card-diagnosis-v2.jpg" alt="卡片排查页展示的状态摘要" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">卡片排查</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
输入内容请去除前后空格并核对末位字符。查询失败时先检查输入值,再重新提交。
|
||||||
|
|
||||||
|
## 扫码查询
|
||||||
|
|
||||||
|
1. 点击“扫码”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 将卡片条码或二维码放入扫描区域。
|
||||||
|
3. 扫描成功后,识别出的内容会自动填入输入框并立即发起查询。
|
||||||
|
|
||||||
|
光线不足、反光或距离不合适时可能扫描失败;此时请调整卡片位置,或改用手动输入。
|
||||||
|
|
||||||
|
## 拍照识别 ICCID
|
||||||
|
|
||||||
|
1. 点击“拍照”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 从相机或相册选择一张卡片图片。
|
||||||
|
3. 确保图片中只有一张卡片,并且 ICCID 完整、清晰、无反光。
|
||||||
|
4. 识别成功后,系统会将 ICCID 填入输入框并继续查询。
|
||||||
|
|
||||||
|
常见提示及处理方式:
|
||||||
|
|
||||||
|
| 提示 | 处理方式 |
|
||||||
|
| --- | --- |
|
||||||
|
| 图片中存在多个可能的 ICCID | 重新拍摄单张卡片 |
|
||||||
|
| 未能识别清晰的 ICCID | 保持卡片平整、对焦清楚后重拍 |
|
||||||
|
| 图片识别超时或请求过于频繁 | 等待后重试,或使用扫码、手动输入 |
|
||||||
|
|
||||||
|
## 首页结果
|
||||||
|
|
||||||
|
首页结果中的“剩余流量”按“剩余 / 套餐总量”展示;在线状态可能为在线、离线、未知或未查询到会话。结果区域的查询和排查说明均提示:内容仅供参考,以业务系统结果为准。
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
title: 小程序端使用手册
|
||||||
|
---
|
||||||
|
|
||||||
|
# 小程序端使用手册
|
||||||
|
|
||||||
|
本文介绍 iotLCM 平台当前已开放的页面及操作功能,具体可用内容以账号实际权限为准。当前版本以**公开查卡和状态排查**为主,业务办理入口暂未开放。
|
||||||
|
|
||||||
|
<div class="mini-program-entry">
|
||||||
|
<figure class="mini-program-entry__qr">
|
||||||
|
<img src="/images/manual/mini-program/mini-program-code.jpg" alt="扫码进入 iotLCM 小程序">
|
||||||
|
<figcaption>扫码进入 iotLCM 小程序</figcaption>
|
||||||
|
</figure>
|
||||||
|
<p class="mini-program-entry__hint">使用微信扫一扫即可打开小程序,体验公开查卡和卡片状态排查功能。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 版本范围
|
||||||
|
|
||||||
|
| 页面或功能 | 当前状态 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 首页 | 已开放 | 手动输入、扫码、拍照识别并查询卡片 |
|
||||||
|
| 卡片详情 | 已开放 | 公开查询只展示卡号、卡状态、在线状态和已用流量 |
|
||||||
|
| 卡片排查 | 已开放 | 输入卡号查看状态摘要;流量趋势仅对管理员账号显示 |
|
||||||
|
| 账号密码登录 | 已开放 | 企业、代理商和终端用户使用已有账号登录 |
|
||||||
|
| 设备列表 | 登录后使用 | 显示当前账号权限范围内的卡片;未登录不要用此页进行公开查卡 |
|
||||||
|
| 办理、订单、工单、预警、开停数据等 | 暂未开放 | 入口或路由会提示“当前版本暂未开放此功能” |
|
||||||
|
| 微信登录、注册、绑定卡片、卡片更多操作 | 暂未开放 | 本版本不提供这些入口 |
|
||||||
|
|
||||||
|
## 推荐流程
|
||||||
|
|
||||||
|
1. 打开小程序首页,在“卡片查询”中输入 ICCID、MSISDN、IMSI 或 IMEI。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 也可以点击“扫码”读取卡片条码,或点击“拍照”识别卡片上的 ICCID。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 点击“查询”,查看卡号、卡状态、使用流量和在线状态。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 需要查看公开字段时点击“详情”;需要判断异常原因时点击“卡片排查”。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/query-actions-v2.jpg" alt="首页查询结果中的详情和卡片排查入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">首页入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/card-details-v2.jpg" alt="卡片详情页展示的公开字段" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">卡片详情</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/card-diagnosis-v2.jpg" alt="卡片排查页展示的状态摘要" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">卡片排查</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
5. 如果需要停复机、开通数据服务、套餐办理、预警或工单,请等待后续版本开放。
|
||||||
|
|
||||||
|
## 账号密码登录
|
||||||
|
|
||||||
|
1. 在需要账号数据的页面进入登录页,或在首页点击登录入口。
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/login-entry-home.jpg" alt="从首页底部的我的入口进入登录" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">首页“我的”入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/login-page-entry.jpg" alt="登录页中的账号密码登录入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">账号密码登录</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. 输入已有账号和密码,完成页面要求的安全验证后点击“登录”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 如果同一账号匹配多个登录身份,按页面提示选择本次要进入的账号。
|
||||||
|
4. 登录成功后返回原页面;设备页会按账号权限加载卡片数据。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
当前版本不开放注册和微信登录。“忘记密码”页面会提示短信服务接入后开放,无法登录时请联系账号管理员或客服。
|
||||||
|
|
||||||
|
## 使用边界
|
||||||
|
|
||||||
|
- 公开查询和排查结果仅供辅助判断,最终以运营商和业务系统状态为准。
|
||||||
|
- 未登录查询不会展示完整 IMSI、IMEI、APN、IP、套餐内部信息等受限字段。
|
||||||
|
- 请勿在查询、排查或反馈内容中提交身份证号、银行卡号等无关敏感信息;密码只应填写在登录表单中。
|
||||||
|
|
||||||
|
## 阅读指引
|
||||||
|
|
||||||
|
- [首页查卡、扫码与拍照](./home)
|
||||||
|
- [卡片详情](./devices)
|
||||||
|
- [卡片排查](./diagnosis)
|
||||||
|
- [常见问题](./faq)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 消息与个人中心
|
||||||
|
|
||||||
|
登录后从底部“我的”进入“消息中心”及账号功能。可见内容按当前账号及接口返回数据决定。
|
||||||
|
|
||||||
|
## 消息中心
|
||||||
|
|
||||||
|
1. 在“我的”点击“消息中心”,查看流量预警、卡片消息、订单通知和系统通知模块及未读数。平台公告等消息可在实际归属的模块中查看,通常归入“系统通知”。
|
||||||
|
|
||||||
|

|
||||||
|
2. 进入模块后,可按消息标题或内容搜索,并选择最新优先、最近30天或时间正序;点击刷新重新加载。
|
||||||
|
3. 点击消息查看详情。进入模块后可将当前模块未读消息全部标记为已读。
|
||||||
|
4. 工单相关通知可跳转工单详情,但仍需工单详情权限;无效链接或无权限时返回消息页并联系管理员。
|
||||||
|
|
||||||
|
“消息提醒”设置入口会转到消息中心。当前版本的流量预警通知通过站内消息查看,不代表会发送微信模板消息或推送。
|
||||||
|
|
||||||
|
## 我的
|
||||||
|
|
||||||
|
- **业务工作台**:有对应权限时显示工单、客户管理和批次执行日志入口。只有工单提交权限时进入提交页面。
|
||||||
|
- **个人资料**:终端用户可编辑个人及客户资料并保存;企业、代理及平台账号只能查看资料,资料变更由管理平台维护。账号安全区域可查看手机号、邮箱、密码入口和微信绑定状态。
|
||||||
|
- **实名认证**:查看实名状态、登记名称、联系电话和客户类型;企业类账号还可查看客户编码及联系地址。该页面用于查看信息。
|
||||||
|
- **微信绑定**:可在微信小程序中绑定、更换或解除当前微信。解除绑定会移除当前微信与账号的关联;操作前先确认可通过密码或短信验证码登录。微信登录入口在本版本仍未开放。
|
||||||
|
- **退出登录**:在“我的”页或个人资料账号安全区域点击退出并确认。
|
||||||
|
|
||||||
|
帮助与反馈页面当前为静态说明入口,不提供在线提交反馈或工单操作;问题请通过工单服务或页面列出的客服联系渠道处理。
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# 工单服务
|
||||||
|
|
||||||
|
登录后可从“办理”或“我的”进入工单服务。卡片详情中的反馈入口可自动带入关联业务并锁定工单类型。列表、提交、详情及处理操作分别受权限控制;只有提交权限的账号仍可直接新建工单。
|
||||||
|
|
||||||
|
## 提交工单
|
||||||
|
|
||||||
|
1. 选择工单类型:卡片、模组、设备、订单、余额或提现问题。从卡片详情进入时,类型由关联业务决定;订单页面当前受版本开关限制。
|
||||||
|
|
||||||
|
<div class="manual-image-grid" style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; width: 100%; max-width: 440px; margin: 24px auto;">
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/work-orders-business-entry-v102.png" alt="从办理页面进入工单服务" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">“办理”中的工单服务入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
<figure style="margin: 0;">
|
||||||
|
<img src="/images/manual/mini-program/work-orders-submit-entry-v102.png" alt="工单列表右上角的提交工单入口" style="display: block; width: 100%; height: auto;">
|
||||||
|
<figcaption style="text-align: center;">工单列表中的提交入口</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. 填写标题、创建人手机号和问题描述。创建人手机号必填,须为有效的11位手机号。
|
||||||
|
3. 标题最多100字,描述最多2000字。最多添加6个附件,每个附件不超过10 MB。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 核对内容并提交。有工单列表权限时,可在列表查看状态;仅有提交权限时,工单由服务部门继续处理。
|
||||||
|
|
||||||
|
附件上传或提交失败时检查网络、文件大小和附件数量后重试。不要反复提交已经显示成功的工单。
|
||||||
|
|
||||||
|
## 查看工单
|
||||||
|
|
||||||
|
列表可按“全部相关”“我提交的”“我收到的”查看(是否出现取决于角色),按全部、已分派、处理中、待确认、已完结或已取消筛选;搜索支持工单编号、标题和内容。使用“加载更多”浏览较早记录。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
点击有详情权限的工单查看描述、附件、状态流转和沟通记录。沟通详情会自动刷新,也可手动刷新或加载历史记录。附件可在支持的格式和平台中预览或下载;旧附件若未保存原始文件名,显示名称可能来自文件地址。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 工单跟进
|
||||||
|
|
||||||
|
页面按当前工单状态和角色显示可执行操作:
|
||||||
|
|
||||||
|
| 操作 | 适用情况 |
|
||||||
|
| --- | --- |
|
||||||
|
| 回复 | 创建人或当前处理方在可沟通状态补充文字或附件 |
|
||||||
|
| 标记解决 | 当前处理方完成处理后通知创建人确认 |
|
||||||
|
| 转单 | 有转单权限的处理方将工单转交给同租户的顶级部门及其负责人;转单后原处理方可能失去办理权限 |
|
||||||
|
| 关单 | 当前处理方在已标记解决、等待创建人确认时,可按权限关闭工单 |
|
||||||
|
| 反馈未解决 | 创建人在待确认时填写未解决原因,工单重新进入处理中 |
|
||||||
|
| 确认解决 | 创建人确认问题已解决,工单完结 |
|
||||||
|
| 取消 | 创建人在允许的处理中状态取消工单;历史记录仍可查看 |
|
||||||
|
| 评价 | 创建人对已完结工单提交一次1至5星评价 |
|
||||||
|
|
||||||
|
**工单进入待确认后,如部署并运行了相应的队列或补偿处理服务,72小时未反馈的工单可能自动完结。**
|
||||||
|
|
||||||
|
查看权限不代表可以办理其他人的工单。没有按钮或提示无权限时,联系账号管理员确认工单角色权限。
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# API 凭证与执行日志
|
||||||
|
|
||||||
|
## 我的 API 凭证
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **我的 API 凭证**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
页面显示本企业的企业 ID、企业名称、`appKey`(API 账号标识)和 `appSecret`(API 密钥),并提供复制按钮。接口鉴权和请求格式请配合 [OpenAPI 接口文档](/openapi/) 使用。
|
||||||
|
|
||||||
|
::: warning appKey ≠ appId
|
||||||
|
- **`appKey`** 和 **`appSecret`** 仅用于调用 `/openapi/v3/query/token` 换取 token
|
||||||
|
- **`appId`**(客户组织编码,如 `CUS-202601-0001`)由 token 接口返回,调用其他接口时在请求体中传入
|
||||||
|
- 不要把 `appKey` 当作 `appId` 使用,两者是不同的概念
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 安全要求
|
||||||
|
|
||||||
|
- `appSecret` 等同接口密码,只能提供给获准的开发和运维人员。
|
||||||
|
- 不要把凭证写入前端代码、公开仓库、工单正文、聊天截图或日志。
|
||||||
|
- 服务端应通过环境变量或密钥管理服务保存凭证。
|
||||||
|
- 不同环境不要共用同一份配置文件。
|
||||||
|
- 怀疑泄露时立即停止相关调用并联系平台支持更换凭证。
|
||||||
|
|
||||||
|
复制后粘贴到配置前,检查是否带入多余空格或换行。接口返回鉴权失败时,先核对企业对应的 appId 与 appSecret 是否配套,再检查签名、时间戳和请求地址。
|
||||||
|
|
||||||
|
## 执行日志(暂未开放)
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **执行日志**。
|
||||||
|
|
||||||
|
批量续费、套餐变更、加油包、重启、停复机、划拨、达量断网等任务提交后,均应通过执行日志确认最终结果。
|
||||||
|
|
||||||
|
### 查询批次
|
||||||
|
|
||||||
|
可按关键字、批次编号、批次类型、异步处理状态和创建时间查询。列表显示:
|
||||||
|
|
||||||
|
- 批次编号和批次类型。
|
||||||
|
- 来源方式,如 Excel 或批量输入。
|
||||||
|
- 运行结果和异步状态。
|
||||||
|
- 总数量、开始时间和结束时间。
|
||||||
|
|
||||||
|
点击批次编号可查看逐卡明细,包括行号、ICCID、MSISDN、IMSI、明细状态和异常原因。点击“查看”可查看批次概要、失败原因、源文件、结果文件和处理时间。
|
||||||
|
|
||||||
|
### 处理失败任务
|
||||||
|
|
||||||
|
1. 用提交时记录的批次号查询。
|
||||||
|
2. 确认批次已结束,避免把“处理中”误判为失败。
|
||||||
|
3. 点击批次号查看失败卡及异常原因。
|
||||||
|
4. 下载结果文件,修正卡号、状态、套餐或模板数据。
|
||||||
|
5. 只重提失败且可修正的卡,不要整批重复提交。
|
||||||
|
6. 无法处理的上游错误通过工单反馈,并附批次号和失败原因。
|
||||||
|
|
||||||
|
常见失败原因包括卡号不属于当前企业、卡状态不允许办理、套餐不匹配、余额不足、模板字段错误、上游超时或重复业务仍在处理中。
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# 业务受理与达量断网
|
||||||
|
|
||||||
|
## 业务受理入口
|
||||||
|
|
||||||
|
左侧菜单 → **通讯管理 → 业务受理**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
页面按“套餐及数据”“生命周期”“预警中心”展示业务入口。每个入口都有独立按钮权限,未授权的入口不会显示。
|
||||||
|
|
||||||
|
可能出现的业务包括:
|
||||||
|
|
||||||
|
| 分类 | 业务 | 用途 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 套餐及数据 | 续费套餐 | 延长当前套餐或购买可续费套餐 |
|
||||||
|
| 套餐及数据 | 变更套餐 | 将卡切换到其他可购套餐 |
|
||||||
|
| 套餐及数据 | 加油包订购 | 为单卡或流量池购买叠加包 |
|
||||||
|
| 套餐及数据 | 开停数据 | 开启或关闭数据通信服务 |
|
||||||
|
| 套餐及数据 | 达量断网 | 设置达到指定流量后断网 |
|
||||||
|
| 生命周期 | 重启 | 向运营商提交卡重启 |
|
||||||
|
| 生命周期 | 自主停机/自主复机 | 暂停或恢复卡服务 |
|
||||||
|
| 生命周期 | 转销户 | 将卡转为销户状态 |
|
||||||
|
| 生命周期 | 划拨 | 将卡划拨给目标下级企业 |
|
||||||
|
| 生命周期 | 退订 | 退订符合条件的套餐或业务 |
|
||||||
|
| 预警中心 | 流量预警配置 | 配置单卡流量阈值预警 |
|
||||||
|
| 预警中心 | 流量池预警配置 | 配置流量池阈值预警 |
|
||||||
|
|
||||||
|
## 通用办理方式(部分开放)
|
||||||
|
|
||||||
|
不同业务会提供以下一种或多种数据来源:
|
||||||
|
|
||||||
|
- **Excel:** 先下载页面提供的最新模板,按模板填写后上传 `.xls` 或 `.xlsx` 文件。不要修改表头或合并单元格。
|
||||||
|
- **批量输入:** 通常每行一个 ICCID;部分业务支持每行填写 `ICCID MSISDN IMSI`。
|
||||||
|
- **连续号段:** 填写 ICCID、MSISDN 或 IMSI 的起始值和结束值,适合连续卡段。
|
||||||
|
|
||||||
|
标准操作流程:
|
||||||
|
|
||||||
|
1. 选择业务入口和数据来源。
|
||||||
|
2. 上传文件或输入卡号,提交预处理。
|
||||||
|
3. 记录系统返回的批次号。
|
||||||
|
4. 核对有效数量、失败数量、目标套餐、购买时长、生效方式、金额和余额等信息。
|
||||||
|
5. 确认无误后提交正式任务。
|
||||||
|
6. 前往“执行日志”查询批次最终状态和失败原因。
|
||||||
|
|
||||||
|
::: warning 提交前确认
|
||||||
|
批量任务可能影响通信、套餐、费用或卡生命周期。提交前应抽查卡号、目标企业、套餐编码、阈值、生效方式和付款方式。不要重复提交仍在处理中的批次。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 续费、变更与加油包(暂未开放)
|
||||||
|
|
||||||
|
### 续费套餐
|
||||||
|
|
||||||
|
支持 Excel 或批量输入。预处理后选择可续费套餐、续费年限、续费日期和付款方式。余额支付时必须确认余额充足;线下支付时按页面要求完成线下确认。
|
||||||
|
|
||||||
|
### 变更套餐
|
||||||
|
|
||||||
|
支持 Excel、批量输入和连续号段。预处理后选择新套餐、购买时长、赠送时长和“立即生效/次月生效”。变更前重点确认生效方式,避免影响当前套餐剩余资源。
|
||||||
|
|
||||||
|
### 加油包订购
|
||||||
|
|
||||||
|
可选择“单卡加油包”或“流量池加油包”。单卡 Excel 需填写 ICCID 和加油包套餐编码;流量池 Excel 需填写流量池编码和套餐编码。预处理后核对数量、单价、总金额、付款账户和余额。
|
||||||
|
|
||||||
|
## 生命周期和数据服务(部分开放)
|
||||||
|
|
||||||
|
重启、自主停机、自主复机、开停数据、转销户和退订通常支持 Excel 或批量输入。生命周期任务提交后不会立即在列表全部完成,应通过批次号在执行日志中跟踪。
|
||||||
|
|
||||||
|
- 重启适合设备长时间不在线且卡状态正常的场景。
|
||||||
|
- 自主停机和关闭数据会中断通信,恢复前请确认业务窗口。
|
||||||
|
- 转销户通常不可逆或需要人工处理,提交前必须复核卡号。
|
||||||
|
- 划拨前确认目标企业,任务完成后卡的数据归属和可见范围会变化。
|
||||||
|
|
||||||
|
## 达量断网批量配置(部分开放)
|
||||||
|
|
||||||
|
在“业务受理”点击“达量断网”,可通过 Excel、批量输入或连续号段批量配置:
|
||||||
|
|
||||||
|
1. 选择开启或关闭。
|
||||||
|
2. 开启时填写阈值,单位为 MB,阈值必须大于 0,最多保留 3 位小数。
|
||||||
|
3. 填写卡号或上传模板后提交。
|
||||||
|
4. 记录批次号并在执行日志中检查结果。
|
||||||
|
|
||||||
|
关闭配置表示不再按该阈值触发断网,不等同于立即恢复所有因其他原因停机的卡。
|
||||||
|
|
||||||
|
## 达量断网页面(部分开放)
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **通讯管理 → 达量断网**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
该页面用于查看和维护已有配置:
|
||||||
|
|
||||||
|
- 可按 MSISDN、ICCID、IMSI、IMEI 或配置状态查询。
|
||||||
|
- 列表显示通道、所属公司、当前套餐、配置状态和阈值。
|
||||||
|
- 有编辑权限时,可修改开启/关闭状态及阈值。
|
||||||
|
- 有删除权限时,可删除该卡的达量断网配置。
|
||||||
|
- 有导出权限时,可按当前条件导出配置。
|
||||||
|
|
||||||
|
批量新增或修改建议从“业务受理 → 达量断网”完成;单卡核对和调整可在本页面完成。
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# 卡信息
|
||||||
|
|
||||||
|
## 页面入口
|
||||||
|
|
||||||
|
左侧菜单 → **通讯管理 → 卡信息**。
|
||||||
|
|
||||||
|
该页面是日常查卡的主要入口,可查询卡的身份标识、运营商状态、套餐、流量、归属企业、通道和到期情况。
|
||||||
|

|
||||||
|
|
||||||
|
## 查询卡片
|
||||||
|
|
||||||
|
常用查询条件如下:
|
||||||
|
|
||||||
|
| 条件 | 用法 |
|
||||||
|
| --- | --- |
|
||||||
|
| 卡号 | 输入 MSISDN、ICCID 或 IMSI 任一标识 |
|
||||||
|
| 卡状态 | 按运营商卡状态筛选 |
|
||||||
|
| IMEI | 按终端设备号查询 |
|
||||||
|
| 所属通道 | 按运营商或接入通道筛选,支持搜索 |
|
||||||
|
| 时间类型、时间范围 | 按所选业务时间区间筛选 |
|
||||||
|
| 查询条件、起始号段、结束号段 | 选择号段类型,并按起止号段范围筛选卡片 |
|
||||||
|
| 备注信息 | 按备注内容筛选 |
|
||||||
|
| 维度、维度状态、维度值 | 按指定业务维度进行组合筛选 |
|
||||||
|
| 排序值、排序类型 | 指定排序字段及升降序 |
|
||||||
|
|
||||||
|
查询不到卡时,先确认使用的是完整卡号,再清空状态、时间和通道等附加条件重试。
|
||||||
|
|
||||||
|
## 列表信息
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| ICCID | SIM 卡芯片的唯一识别码,业务办理和故障定位最常用 |
|
||||||
|
| MSISDN | 运营商分配的移动号码,也常称卡号 |
|
||||||
|
| IMSI | SIM 卡在移动网络中的用户识别码 |
|
||||||
|
| 卡状态 | 运营商侧当前生命周期状态 |
|
||||||
|
| 类型 | “单卡”表示独立计量,“池卡”表示参与流量池共享 |
|
||||||
|
| 通道 | 卡接入的运营商或上游通道 |
|
||||||
|
| 所属公司 | 当前卡归属的企业 |
|
||||||
|
| 套餐 | 当前主套餐名称 |
|
||||||
|
| 总流量、已用流量、剩余流量 | 当前套餐流量,单位为 MB |
|
||||||
|
| 到期时间 | 当前套餐预计到期日期 |
|
||||||
|
| 出库时间 | 卡片分配出库日期 |
|
||||||
|
| 备注信息 | 企业维护的业务备注 |
|
||||||
|
|
||||||
|
列表中的流量不是实时会话流量。需要最新结果时,执行“同步用量”后再刷新。
|
||||||
|
|
||||||
|
## 查看卡详情
|
||||||
|
|
||||||
|
点击目标卡右侧“查看”。详情上方展示:MSISDN、ICCID、IMSI、IMEI、卡状态、网络类型、流量同步时间、套餐总流量、到期时间、出库时间、当前套餐、已用流量、剩余流量、所属企业、所属通道、达量断网配置状态、限额、备注。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
详情下方包含以下页签:
|
||||||
|
|
||||||
|
- **会话信息:** APN、IPv4、IPv6、接入方式、在线状态、会话建立时间、实时 IMEI、开关机状态等。点击“刷新”重新查询;“未查询到会话”通常表示设备当前未建立数据会话,并不等同于卡已销户。
|
||||||
|
- **归属信息:** 当前套餐编号、套餐名称、所属企业和所属通道(所属通道不予展示)。
|
||||||
|
- **主套餐:** 订单编号、套餐名称、套餐类型、订购状态、生效时间和到期时间。
|
||||||
|
- **叠加套餐:** 加油包名称、总流量、支付方式和交易时间。
|
||||||
|
- **变更历史:** 变更前后套餐、订购状态和办理时间。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 常用操作
|
||||||
|
|
||||||
|
页面上的操作按钮以账号权限为准。
|
||||||
|
|
||||||
|
- **备注:** 编辑本企业对卡片的业务备注,便于按项目、设备或使用场景检索。
|
||||||
|
- **同步用量:** 向上游请求最新用量。同步成功后查看“流量同步时间”是否更新。
|
||||||
|
- **同步状态:** 向上游请求最新运营商卡状态。
|
||||||
|
- **导出:** 按当前筛选条件导出卡信息。
|
||||||
|
- **诊断:** 在卡详情点击“诊断”,系统会携带当前 ICCID 跳转到智能诊断并自动查询。
|
||||||
|
|
||||||
|
“批量操作”当前仍显示为待接入功能。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 查看数据异常时
|
||||||
|
|
||||||
|
1. 先核对卡号和所属企业。
|
||||||
|
2. 查看卡状态、套餐到期时间及流量同步时间。
|
||||||
|
3. 分别执行“同步状态”和“同步用量”。
|
||||||
|
4. 打开详情刷新会话信息。
|
||||||
|
5. 进入[智能诊断](./intelligent-diagnosis)做完整排查。
|
||||||
|
6. 仍有异常时创建工单,并提供 ICCID、异常时间和已完成的操作。
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 通道与接入管理
|
||||||
|
|
||||||
|
本章仅供有接入维护权限的平台管理员使用。企业日常查卡、诊断及业务受理通常不需要修改通道。入口为左侧菜单中的“通道模板”和“通道管理”。
|
||||||
|
|
||||||
|
## 通道模板
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 按模板标识、名称、模型或状态查询已有模板,优先使用已确认可用的模板。
|
||||||
|
2. 新增时填写模板标识、名称、模型、状态及模板配置。
|
||||||
|
3. 模型包括 `shared_token` 和 `direct`;必须与实际上游接入方式匹配。
|
||||||
|
4. 模板配置按对应接入实现要求填写有效 JSON,保存后重新查看配置。
|
||||||
|
|
||||||
|
模板影响引用它的通道。变更前核对关联通道和兼容性,不在公开手册、截图或工单里展示真实密钥。
|
||||||
|
|
||||||
|
## 新增与维护通道
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 在“通道管理”新增,填写通道名称、别名、编号、运营商及模板。
|
||||||
|
2. 按模板要求配置应用标识、密钥、地址等接入信息,不将凭证填入名称或备注。
|
||||||
|
3. 分别核对通道状态、是否正常、是否轮询、共享 Token 及对接信息;这些配置含义不同,不能互相代替。
|
||||||
|
4. 保存后打开详情核对,并使用通道测试验证上游返回。
|
||||||
|
5. 需要时使用“刷新卡数量”,再核对库存、已出库及已销户数量。
|
||||||
|
|
||||||
|
未开启共享 Token 的通道修改编号时,系统会提示迁移关联数据并刷新通道实例。确认前核对旧编号、新编号和关联业务,不把它当作仅修改显示名称。已开启共享 Token 的通道不能在页面修改编号。
|
||||||
|
|
||||||
|
## 通道测试
|
||||||
|
|
||||||
|
1. 在目标通道点击“测试”,选择“单码直测”“单码补全”或“完整三码”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 单码模式选择 ICCID、IMSI 或 MSISDN 并输入对应值;“单码直测”直接使用所填号码调用查询接口,“单码补全”会先查询完整卡信息再测试。“完整三码”需填写同一张卡的 ICCID、IMSI 和 MSISDN。部分电信接口只能按 MSISDN 查询,测试前应选用适合该通道的模式。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 选择全部测试项或单项测试。测试项包括基础信息、实时会话、卡状态、停机原因、冻结、机卡绑定、设备、区域限制、IMEI、流量及 APN 等。
|
||||||
|
4. 执行后逐项查看成功、失败或超时结果。需要重试时先核对失败原因,只重试相关项目。
|
||||||
|
|
||||||
|
某一查询成功不代表所有上游能力均已支持。测试失败时核对模板、凭证、请求地址、上游授权和卡片所属通道,避免高频重复请求。
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# 企业、用户与角色
|
||||||
|
|
||||||
|
## 企业信息
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **企业管理 → 企业信息**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
该页面用于维护当前企业资料,分为单位信息和实名认证信息。
|
||||||
|
|
||||||
|
单位信息通常包括联系电话、邮箱、统一社会信用代码、组织所在地和详细地址。实名认证信息包括单位全称、行业、法人姓名、法人身份证号、单位类型、成立时间、开户行名称和开户行账号。
|
||||||
|
|
||||||
|
单位信息如需修改请联系管理员。
|
||||||
|
|
||||||
|
## 角色管理
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **企业管理 → 角色管理**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
角色用于把菜单和按钮权限批量授予用户。列表可按角色名称、权限字符和状态查询。
|
||||||
|
|
||||||
|
### 创建角色
|
||||||
|
|
||||||
|
1. 点击“新增”。
|
||||||
|

|
||||||
|
2. 填写角色名称和角色权限字符。
|
||||||
|
3. 设置角色状态。
|
||||||
|
4. 在“菜单权限”中勾选该角色需要使用的页面和操作。
|
||||||
|
5. 需要传递给直属下级企业时,按业务要求设置“下发给下一级”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. 保存后回到“用户管理”,将角色分配给用户。
|
||||||
|

|
||||||
|
|
||||||
|
本级机构绑定的默认角色不能修改或删除。新增自定义角色时遵循最小权限原则,不要为普通查询人员授予企业删除、用户删除、角色修改或批量业务提交权限。
|
||||||
|
|
||||||
|
## 企业客户
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **企业管理 → 企业客户**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
该页面面向获准管理下级企业的客户。可按企业名称和状态查询,列表显示企业名称、登录账号、负责人、联系电话、邮箱、组织所在地、详细地址、创建时间和状态。
|
||||||
|
|
||||||
|
根据按钮权限,可执行:
|
||||||
|
|
||||||
|
- **新增:** 创建直属企业客户之前,需在角色管理界面创建该企业客户同名角色,之后返回新增页面设置登录账号、初始密码、联系方式和企业地址等信息。
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
- **修改:** 调整企业名称、登录账号、负责人。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **重置密码:** 为该企业主账号设置新密码,密码长度为 5 至 20 位。
|
||||||
|

|
||||||
|
- **企业管理:** 分两步维护单位信息和实名认证信息。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **删除:** 删除前确认该企业没有仍在使用的卡、用户和业务数据。删除属于高风险操作。
|
||||||
|

|
||||||
|
|
||||||
|
新增企业后,应及时通知对方修改初始密码,并按需要创建角色和用户。
|
||||||
|
|
||||||
|
## 用户管理
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **企业管理 → 用户管理**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可按用户名称、昵称、联系电话、状态和创建时间查询。列表显示用户名称、昵称、角色、联系电话、邮箱、状态和创建时间。
|
||||||
|
|
||||||
|
### 新增用户
|
||||||
|
|
||||||
|
1. 点击“新增”。
|
||||||
|
2. 填写用户名称、初始密码、昵称、联系电话、邮箱、性别和备注。
|
||||||
|
3. 设置账号状态。
|
||||||
|
4. 至少选择一个角色后保存。
|
||||||
|
|
||||||
|
初始密码长度为 5 至 20 位。建议使用更长且不重复的密码,并要求用户首次登录后修改。
|
||||||
|

|
||||||
|
|
||||||
|
### 维护用户
|
||||||
|
|
||||||
|
- “修改”可调整资料、状态和角色。
|
||||||
|
- “重置密码”可设置新密码。
|
||||||
|
- “删除”会使用户失去登录和数据访问能力,操作前确认账号不再使用。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 建议的角色划分
|
||||||
|
|
||||||
|
| 角色 | 建议权限 |
|
||||||
|
| --- | --- |
|
||||||
|
| 查询人员 | 卡信息查看、智能诊断,只读查看执行日志 |
|
||||||
|
| 业务操作员 | 查询权限,以及已批准的业务受理按钮 |
|
||||||
|
| 工单人员 | 我的工单、沟通、工单记录 |
|
||||||
|
| 接口开发 | 我的 API 凭证,只读卡信息;凭证按需单独授权 |
|
||||||
|
| 企业管理员 | 用户、角色和企业资料管理;高风险权限严格控制 |
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# 常见问题
|
||||||
|
|
||||||
|
## 登录后看不到某个菜单
|
||||||
|
|
||||||
|
平台按角色显示菜单。先联系本企业管理员检查用户是否绑定正确角色、角色是否正常以及菜单权限是否勾选。若整个企业都没有该功能,需要联系平台支持开通。
|
||||||
|
|
||||||
|
## 同事有按钮,我没有
|
||||||
|
|
||||||
|
页面权限和按钮权限分别控制。即使都能进入同一页面,新增、编辑、导出、同步、删除或业务提交按钮也可能不同。由企业管理员对比双方角色权限。
|
||||||
|
|
||||||
|
## 卡信息查询不到卡
|
||||||
|
|
||||||
|
1. 清空其他筛选条件。
|
||||||
|
2. 使用完整 MSISDN、ICCID 或 IMSI 查询。
|
||||||
|
3. 检查是否有前后空格或字符缺失。
|
||||||
|
4. 确认该卡已划拨到当前企业,且账号有相应数据范围。
|
||||||
|
5. 仍无结果时创建工单并提供卡号。
|
||||||
|
|
||||||
|
## 卡状态或流量没有更新
|
||||||
|
|
||||||
|
在“卡信息”中分别执行“同步状态”和“同步用量”,完成后刷新列表,并查看同步时间是否变化。运营商接口可能有延迟;短时间内不要反复同步。
|
||||||
|
|
||||||
|
## 智能诊断显示未查询到会话
|
||||||
|
|
||||||
|
表示当前没有查到数据会话,不一定是卡异常。检查设备供电、信号、APN 和天线,确认数据服务已开启后重启设备,再稍后诊断。
|
||||||
|
|
||||||
|
## 诊断提示运营商不支持
|
||||||
|
|
||||||
|
当前卡所属通道未提供该项实时查询能力。结合卡信息、同步时间、设备侧日志和其他诊断模块判断;需要运营商侧核实时提交工单。
|
||||||
|
|
||||||
|
## 批量业务提交后一直没有结果
|
||||||
|
|
||||||
|
记录批次号,进入“执行日志”查看异步状态。仍在处理中时请等待;已失败时查看批次明细和失败原因。不要在结果未明确前重复提交同一批卡。
|
||||||
|
|
||||||
|
## Excel 上传失败
|
||||||
|
|
||||||
|
- 使用页面当次下载的最新模板。
|
||||||
|
- 文件格式必须为 `.xls` 或 `.xlsx`,且不超过页面限制。
|
||||||
|
- 不修改表头、不合并单元格、不增加公式。
|
||||||
|
- 卡号列按文本格式保存,避免科学计数法或末位被修改。
|
||||||
|
- 根据失败提示修正后,仅重提失败数据。
|
||||||
|
|
||||||
|
## 达量断网开启后没有立即断网
|
||||||
|
|
||||||
|
确认阈值单位为 MB、配置任务已成功、卡用量已同步且确实达到阈值。运营商侧执行可能有延迟;同时检查卡是否受其他套餐或通道策略影响。
|
||||||
|
|
||||||
|
## 如何提交高质量工单
|
||||||
|
|
||||||
|
提供完整 ICCID、MSISDN、故障开始时间、影响数量、设备位置、复现步骤、诊断结果、执行日志批次号和必要截图。描述中避免使用“卡有问题”这类无法定位的表述,也不要发送密码或 API 凭证。
|
||||||
|
|
||||||
|
## 使用安全建议
|
||||||
|
|
||||||
|
- 为每位操作人员创建独立账号,不共享管理员账号。
|
||||||
|
- 查询人员、业务操作员和管理员使用不同角色。
|
||||||
|
- 定期停用离职或不再使用的账号。
|
||||||
|
- 高风险批量任务实行复核,保留批次号和结果文件。
|
||||||
|
- 导出的卡信息、企业资料和 API 凭证按敏感数据管理。
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 首页与通用操作
|
||||||
|
|
||||||
|
## 首页(暂未开放)
|
||||||
|
|
||||||
|
**入口:** 登录后默认进入“首页”;如已授权,也可点击左侧“首页”。
|
||||||
|
|
||||||
|
首页用于快速掌握企业卡业务概况,通常包括:
|
||||||
|
|
||||||
|
- SIM 卡总数、本月新增、本月数据用量、本月短信用量和 SIM 卡月活跃度。
|
||||||
|
- 生命周期分布、流量池、物联卡位置分布等图表。
|
||||||
|
- 欠费停机、主动停机、机卡分离停机、强制停机等业务预警。
|
||||||
|
- 流量用尽关停、限额管控关停、封顶关停和套餐余量提醒等流量预警。
|
||||||
|
- 卡信息、智能诊断等快捷入口。
|
||||||
|
|
||||||
|
首页数据用于概览和发现异常。处理单张卡时,应进入“卡信息”查看最新详情;预警统计与明细可能存在同步时间差。
|
||||||
|
|
||||||
|
## 列表页通用操作
|
||||||
|
|
||||||
|
### 查询与重置
|
||||||
|
|
||||||
|
1. 在查询区填写一个或多个条件。
|
||||||
|
2. 点击“查询”;部分页面修改条件后会自动查询。
|
||||||
|
3. 条件过多导致无结果时,点击“重置”后只保留卡号、批次号等关键条件重试。
|
||||||
|
|
||||||
|
卡号输入框通常支持 MSISDN、ICCID、IMSI。输入前后不要带空格;批量输入时一般每行一张卡。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 表格工具
|
||||||
|
|
||||||
|
列表右上角可能提供刷新、列设置、密度或全屏等工具。横向字段较多时可左右滚动,固定在右侧的“操作”列用于查看或办理业务。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 导出(暂未开放)
|
||||||
|
|
||||||
|
“导出”通常按当前查询条件导出,而不是只导出当前页。建议先缩小查询范围再导出,并妥善保管包含卡号、设备号和企业信息的文件。
|
||||||
|
|
||||||
|
### 状态与数据时间
|
||||||
|
|
||||||
|
卡状态、流量、会话和执行结果来自不同上游接口,更新时间可能不同。判断数据是否最新时,优先查看“同步时间”“开始时间”“结束时间”等时间字段,不要只看页面打开时间。
|
||||||
|
|
||||||
|
## 权限与数据范围
|
||||||
|
|
||||||
|
- 菜单权限决定页面是否显示。
|
||||||
|
- 按钮权限决定是否能新增、编辑、导出、同步或删除。
|
||||||
|
- 数据权限决定能看到本企业、下级企业还是仅本人数据。
|
||||||
|
- 同一企业内不同用户看到的菜单和数据可能不同。
|
||||||
|
|
||||||
|
企业管理员可通过“企业管理 → 用户管理”和“角色管理”调整本企业用户权限。涉及平台未向企业开放的功能时,需要联系平台支持。
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# Web 端使用手册
|
||||||
|
|
||||||
|
本手册面向 iotLCM Web 管理平台的企业客户,说明登录平台后各页面的入口、信息含义和常用操作。
|
||||||
|
|
||||||
|
::: info 权限说明
|
||||||
|
平台按企业、角色和用户分配权限。左侧菜单、页面按钮可能因企业实际授权不同而不同;看不到某项功能时,先联系本企业管理员确认角色权限。按钮以页面实际显示为准。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 登录平台
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 访问 [iotLCM 管理平台](https://admin.example.com/)。
|
||||||
|
2. 输入平台分配的账号和密码登录。
|
||||||
|
3. 首次登录后建议立即修改初始密码,并确认绑定的手机号、邮箱是否正确。
|
||||||
|
4. 登录后通过左侧菜单进入业务页面;顶部标签可在已打开页面间切换。
|
||||||
|
|
||||||
|
若提示账号停用、密码错误或无可用菜单,请联系本企业管理员;企业管理员无法处理时,可通过工单联系平台支持。
|
||||||
|
|
||||||
|
## 功能地图(部分开放)
|
||||||
|
|
||||||
|
| 菜单 | 页面 | 主要用途 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 顶级菜单 | 首页 | 查看卡量、用量、生命周期和预警概览 |
|
||||||
|
| 通讯管理 | 业务受理 | 批量办理套餐、数据服务、生命周期和预警业务 |
|
||||||
|
| 通讯管理 | 卡信息 | 查询卡、查看套餐和会话、同步状态与用量 |
|
||||||
|
| 通讯管理 | 达量断网 | 查询、编辑和导出单卡达量断网配置 |
|
||||||
|
| 通讯管理 | 智能诊断 | 按 MSISDN、ICCID 或 IMSI 一键排查卡和网络状态 |
|
||||||
|
| 企业管理 | 企业信息 | 维护本企业基础资料和实名认证资料 |
|
||||||
|
| 企业管理 | 用户管理 | 管理本企业用户及其角色 |
|
||||||
|
| 企业管理 | 角色管理 | 配置角色菜单权限和状态 |
|
||||||
|
| 企业管理 | 企业客户 | 管理直属企业客户、登录账号和企业资料 |
|
||||||
|
| 工单管理 | 我的工单 | 创建工单、沟通、跟踪处理和评价 |
|
||||||
|
| 工单管理 | 工单记录 | 查询工单的状态变更与操作轨迹 |
|
||||||
|
| 顶级菜单 | 我的 API 凭证 | 查看并复制本企业接口凭证 |
|
||||||
|
| 顶级菜单 | 执行日志 | 跟踪批量任务结果及逐卡失败原因 |
|
||||||
|
|
||||||
|
“工单问题沟通”是从“我的工单”进入的详情页面,不在左侧单独显示。
|
||||||
|
|
||||||
|
## 推荐使用流程
|
||||||
|
|
||||||
|
### 日常查卡
|
||||||
|
|
||||||
|
1. 进入“通讯管理 → 卡信息”。
|
||||||
|
2. 用 MSISDN、ICCID、IMSI 或 IMEI 查询目标卡。
|
||||||
|
3. 打开“查看”,核对卡状态、套餐、用量、到期时间和同步时间。
|
||||||
|
4. 数据较旧时使用“同步用量”或“同步状态”,再刷新查看。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 排查不能联网
|
||||||
|
|
||||||
|
1. 从卡详情点击“诊断”,或进入“通讯管理 → 智能诊断”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 输入卡号并执行“一键诊断”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 依次查看 SIM 卡状态、通信服务、实时流量、策略限制和网络连接。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 仍无法定位时创建工单,并附上卡号、故障时间、诊断结果和终端现象。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 办理批量业务(暂未开放)
|
||||||
|
|
||||||
|
1. 进入“通讯管理 → 业务受理”,选择已授权的业务。
|
||||||
|
2. 下载最新 Excel 模板,或使用批量输入、连续号段方式提交卡号。
|
||||||
|
3. 对需要预处理的业务先核对有效数量、失败项、套餐、价格和生效方式。
|
||||||
|
4. 确认提交后记录批次号。
|
||||||
|
5. 进入“执行日志”按批次号查看最终结果。
|
||||||
|
|
||||||
|
## 阅读指引
|
||||||
|
|
||||||
|
- [首页与通用操作](./home):了解查询、重置、导出和数据刷新规则。
|
||||||
|
- [卡信息](./card-info):了解卡列表字段、卡详情和会话信息。
|
||||||
|
- [智能诊断](./intelligent-diagnosis):了解入口、诊断步骤和结果判断。
|
||||||
|
- [业务受理与达量断网](./business-operations):了解批量业务及达量断网配置。
|
||||||
|
- [企业、用户与角色](./enterprise-management):了解组织和账号权限管理。
|
||||||
|
- [工单管理](./work-orders):了解问题提交、沟通、关闭和评价。
|
||||||
|
- [API 凭证与执行日志](./api-and-logs):了解凭证保管和批次追踪。
|
||||||
|
- [常见问题](./faq):快速排查菜单、数据、任务和诊断问题。
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# 智能诊断
|
||||||
|
|
||||||
|
## 入口
|
||||||
|
|
||||||
|
有两种进入方式:
|
||||||
|
|
||||||
|
1. 左侧菜单 → **通讯管理 → 智能诊断**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 左侧菜单 → **通讯管理 → 卡信息** → 找到目标卡 → “查看” → “诊断”。第二种方式会自动带入 ICCID 并开始查询。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 开始诊断
|
||||||
|
|
||||||
|
1. 输入 MSISDN、ICCID 或 IMSI 任一完整标识。
|
||||||
|
2. 点击“一键诊断”。
|
||||||
|
3. 等待页面提示“诊断完成”。不同上游能力返回速度可能不同,请勿连续重复点击。
|
||||||
|
4. 展开各诊断模块查看状态、详细字段和提示。
|
||||||
|
|
||||||
|
诊断完成后,页面顶部显示 MSISDN、ICCID、IMSI 和实时 IMEI;下方同时查询近 7 天、近 30 天和近 6 个月的流量趋势。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 诊断结果
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### SIM 卡状态
|
||||||
|
|
||||||
|
关注卡状态、停机原因、是否冻结、冻结原因和冻结操作时间。若卡处于停机、冻结、待激活或销户状态,应先处理卡生命周期问题,再排查终端网络。
|
||||||
|
|
||||||
|
### 通信服务
|
||||||
|
|
||||||
|
关注 APN、IPv4、IPv6、通信服务状态、在线状态、会话状态、会话建立时间、接入方式和实时 IMEI。
|
||||||
|
|
||||||
|
- 通信服务关闭时,需先办理开通或复机。
|
||||||
|
- 没有会话时,检查设备是否开机、天线和信号是否正常、APN 是否正确。
|
||||||
|
- 实时 IMEI 与预期设备不一致时,确认是否换机、换卡或发生机卡分离。
|
||||||
|
|
||||||
|
### 实时流量
|
||||||
|
|
||||||
|
展示已用流量、剩余流量和同步时间。历史趋势用于判断设备是否持续上报、是否出现突增或长时间无流量。
|
||||||
|
|
||||||
|
### 策略限制
|
||||||
|
|
||||||
|
展示区域限制订购、触发状态、限制区域和人联网访问限制等。卡和设备均正常但仍无法联网时,应重点检查此模块。
|
||||||
|
|
||||||
|
### 网络连接
|
||||||
|
|
||||||
|
展示开关机状态、设备状态和机卡绑定状态。出现关机、设备异常或机卡分离时,优先在终端侧核查供电、信号、插卡和绑定关系。
|
||||||
|
|
||||||
|
## 推荐排查顺序
|
||||||
|
|
||||||
|
1. **卡是否可用:** 卡状态、冻结和停机原因。
|
||||||
|
2. **服务是否开启:** 数据通信服务和套餐是否有效。
|
||||||
|
3. **流量是否充足:** 剩余流量、达量限制和到期时间。
|
||||||
|
4. **策略是否限制:** 区域、人联网及其他策略。
|
||||||
|
5. **设备是否在线:** 开关机、设备状态、会话和实时 IMEI。
|
||||||
|
6. **趋势是否异常:** 对比近 7 天、30 天、6 个月流量。
|
||||||
|
|
||||||
|
## 结果提示说明
|
||||||
|
|
||||||
|
- **查询成功但未返回数值:** 上游已响应,但本次没有可展示的数据;结合同步时间和其他模块判断。
|
||||||
|
- **未查询到会话:** 当前没有在线数据会话,可稍后重试并检查终端。
|
||||||
|
- **运营商暂不支持/不支持查询:** 当前通道没有对应实时查询能力,不代表卡一定异常。
|
||||||
|
- **查询暂时未完成:** 可能是上游忙、缓存或接口超时,稍后重试。
|
||||||
|
- **系统无记录:** 先检查卡号是否完整以及卡是否归属当前企业。
|
||||||
|
|
||||||
|
需要提交工单时,建议附上诊断时间、完整 ICCID、异常模块、终端所在地、设备指示灯或日志,以及问题首次出现时间。
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# 入库、库存与出库
|
||||||
|
|
||||||
|
本章供获授权的库存及业务管理员使用。在左侧菜单查找“入库管理”“库存卡信息”“批次管理”和“销户卡信息”。这些操作可能改变卡片归属、套餐和订单,请先确认目标客户与卡片范围。
|
||||||
|
|
||||||
|
## 入库
|
||||||
|
|
||||||
|
1. 进入“入库管理”,先查询既有批次,确认卡片未重复录入。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 选择 Excel、批量输入或连续号段入口;Excel 使用页面下载的当前模板,输入方式按页面要求填写卡标识。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 填写通道、上游套餐、卡类型、协议到期等表单要求的信息;按需选择是否校验上游基础信息。
|
||||||
|
4. 核对通道及数量后提交,保存返回的批次号。
|
||||||
|
5. 在执行日志查看处理结果,在库存卡信息中抽查成功卡片;失败卡按原因修正后再处理。
|
||||||
|
|
||||||
|
入库只是将卡纳入库存管理,不等于已销售给客户,也不表示已开通通信或订购套餐。
|
||||||
|
|
||||||
|
## 查询库存
|
||||||
|
|
||||||
|
按 ICCID、MSISDN、IMSI、通道、上游套餐、卡类型、协议到期区间及创建时间查询。优先检查入库批次号、协议到期时间和通道是否正确,再决定出库范围。
|
||||||
|
|
||||||
|
号段查询与输入须选择正确的号码类型,卡号应保留完整字符。不要在 Excel 中把长卡号转换成科学计数法或丢掉前导零。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 出库
|
||||||
|
|
||||||
|
库存页面提供批量输入、号段及按批次出库等入口,具体以权限为准。按批次出库按“选择批次 → 确认卡 → 填写信息”完成。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 选择要出库的库存批次或卡片,核对数量及每张卡的状态。
|
||||||
|
2. 选择客户/机构与业务套餐,填写套餐开始时间、购买时长和赠送时长,检查预计结束日期。
|
||||||
|
3. 核对订单金额、备注和付款安排。正常余额方式会校验客户余额账户存在、状态正常且余额充足。
|
||||||
|
4. 若线下合同、销售及走账确已完成,才按实际业务勾选“线下合同/销售/走账已完成”。不要用该选项绕过未完成的付款处理。
|
||||||
|
5. 确认后提交,记录批次号和相关订单信息。
|
||||||
|
6. 在执行日志核对结果,成功后到“卡信息”确认客户归属、套餐和到期时间,并在订单页面核对交易记录。
|
||||||
|
|
||||||
|
余额不足或账户异常时先联系财务及管理员处理,不要多次提交相同卡片。提交超时也应先查原批次。
|
||||||
|
|
||||||
|
## 批次及销户记录
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- “批次管理”用于查询批次信息,并通过页面入口查看批次卡片;实际执行状态仍需对照执行日志。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- “销户卡信息”用于查询已进入销户记录的卡片,有权限时可导出。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- 申请转销户从[业务受理](./business-operations)进入,不能把查询销户记录视为恢复服务入口。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 现象 | 核对方向 |
|
||||||
|
| --- | --- |
|
||||||
|
| 入库失败或重复 | 原批次结果、卡号格式、通道及库存已有记录 |
|
||||||
|
| 库存中找不到卡 | 是否已出库、查询条件及账号数据权限 |
|
||||||
|
| 出库不能提交 | 必填项、客户/套餐选择、金额及余额账户状态 |
|
||||||
|
| 出库后归属或套餐不符 | 对照原批次与订单,不要另建任务覆盖问题,联系管理员核实 |
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# 套餐配置与定价
|
||||||
|
|
||||||
|
本章面向获授权的产品、定价及企业管理人员。请在左侧菜单查找同名页面;实际菜单分组以部署配置为准。普通查卡账号不需要维护这些配置。
|
||||||
|
|
||||||
|
## 套餐配置顺序
|
||||||
|
|
||||||
|
套餐分类和上游套餐用于组织产品及上游资源;流量套餐定义对客户提供的产品;定价用于确定不同客户层级的价格。维护产品后,还要配置允许续费、变更和加油包的关系,业务受理页才可能出现相应选项。
|
||||||
|
|
||||||
|
1. 在“套餐分类”查询或新增分类,填写表单必填项并保存。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 在“上游套餐”查询对应上游产品,核对编码、名称及关联信息;需要新增或修改时由掌握上游资料的管理员操作。
|
||||||
|
3. 在“流量套餐信息”新增产品,选择单卡套餐、单卡加油包、池卡套餐或池卡加油包。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. 填写套餐名称、编码、计费方式、分类、流量(MB)、启用状态及价格相关字段。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5. 设置允许的续费时长、折扣、可续费套餐、可变更套餐和可订购加油包;需要续费本套餐时检查对应开关。
|
||||||
|
6. 保存后重新查询并查看详情,确认产品类型和关联编码正确,再供业务人员使用。
|
||||||
|
|
||||||
|
可配置自动续费套餐和年限时,应同时核对自动续费开关。保存配置仅代表规则已保存,实际自动处理还依赖部署的服务与任务运行情况。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 问题 | 核对方向 |
|
||||||
|
| --- | --- |
|
||||||
|
| 新建套餐后业务页不可选 | 启用状态、套餐类型、当前套餐的允许办理关系及客户价格 |
|
||||||
|
| 调价后显示价格没有变化 | 当前选中的机构层级、价格来源及是否存在客户独立覆盖 |
|
||||||
|
| 流量池用量与单卡合计暂不一致 | 数据同步时间、统计范围及卡片归属,先刷新后再比较 |
|
||||||
|
| 无维护按钮 | 产品、定价、查询和导出可能分别授权,请联系管理员 |
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# 预警管理
|
||||||
|
|
||||||
|
**适用人员:** 获授权的企业管理员和业务人员。左侧菜单查找预警中心-卡配置-新增预警或预警中心-流量池配置-新增预警;批量设置也可从“通讯管理 → 业务受理 → 预警中心”进入。
|
||||||
|
|
||||||
|
预警用于在达到阈值时提醒;[达量断网](./business-operations)用于限制通信,两者需要分别核对。保存配置成功不代表已经发送通知或执行断网。
|
||||||
|
|
||||||
|
## 新增或编辑配置
|
||||||
|
|
||||||
|
1. 选择单卡或流量池配置页面,按对象编码及状态查询,避免重复维护。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. 点击“新增”,或对已有记录点击“修改”。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. 填写对应卡号或流量池编号。单卡使用流量阈值(MB),流量池使用百分比;不要混用单位。
|
||||||
|
4. 填写第一次提醒阈值,按需设置第二、第三次提醒,按表单与服务端提示校验各档数值。
|
||||||
|
5. 核对提醒方式。站内提醒为基础方式;选择邮箱或短信时,填写相应接收地址或手机号。
|
||||||
|
6. 设置启用状态和备注,保存后重新查询并查看详情。
|
||||||
|
|
||||||
|
接收人填写错误、短信或邮件服务未配置,都可能导致外部通知无法送达。不要将配置存在等同于通知已送达;必要时请管理员核查通知记录与服务状态。
|
||||||
|
|
||||||
|
## 启停、删除与批量配置
|
||||||
|
|
||||||
|
- 列表可以按权限启用、停用、查看、修改或删除规则;删除前保留必要业务记录。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- 停用规则表示不再按该规则继续触发,不会撤销已执行的通信操作。
|
||||||
|
- 批量配置从业务受理选择单卡或流量池预警。单卡提供 Excel、输入及号段方式;流量池不支持连续号段。Excel 使用当前模板填写。
|
||||||
|
- 提交后记录批次号,前往[执行日志](./api-and-logs)核对结果,再抽查配置页面。
|
||||||
|
|
||||||
|
## 未收到提醒时
|
||||||
|
|
||||||
|
依次检查对象编码、单位、阈值、启用状态、用量同步时间和接收人;仍有异常时提供对象编码、配置时间、预期触发档位和批次号创建工单。提交工单时不要附带消息服务密钥。
|
||||||
|
|
||||||
|
## 告警与通知记录
|
||||||
|
|
||||||
|
预警是否真的发出了提醒,要通过发送记录核对,不能只看配置是否存在。
|
||||||
|
|
||||||
|
| 页面 | 主要字段 | 用途 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 消息日志 | 消息类型(系统通知、活动通知、订单通知、账户通知)、接收用户、消息标题、消息内容、跳转链接、发送时间、是否撤回 | 核对站内消息是否写入、发给了哪些用户、是否被撤回 |
|
||||||
|
| 通知日志 | 通知标题、通知内容、发送用户类型(代理商/终端用户)、发送时间 | 核对面向客户的通知记录 |
|
||||||
|
|
||||||
|
- 两个页面都支持按标题和发送时间查询,有权限时可导出当前查询结果。
|
||||||
|
- 当前版本没有独立的“告警记录”页面。判断一次预警是否触发,要结合预警配置、消息日志和通知日志一起看:配置决定阈值和接收人,日志决定有没有发出去。
|
||||||
|
- 日志中出现记录,说明消息已写入并进入发送流程;短信、邮件是否送达仍取决于部署的消息服务和登记的联系方式,这两个页面不能替代短信服务商的发送回执。
|
||||||
|
|
||||||
|
## 套餐到期提醒
|
||||||
|
|
||||||
|
套餐到期提醒与上面的流量阈值配置不同:本地服务按有效单卡套餐的到期时间自动扫描,默认在到期前 30 天、15 天、7 天分档判断。阈值由服务端配置决定,当前 Web 的“卡预警配置”表单只提供流量阈值(MB),不提供到期天数设置。
|
||||||
|
|
||||||
|
收到提醒后,先在[卡信息](./card-info)核对到期日期、套餐和归属企业,再决定是否办理续费。站内消息会按符合条件的账号写入消息中心;短信、邮件是否发送取决于已登记的联系方式和部署的消息服务。未收到时,请管理员检查通知或消息日志及后台任务,不要只根据首页的固定预警面板判断。
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 工单管理
|
||||||
|
|
||||||
|
## 我的工单
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **工单管理 → 我的工单**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可按工单编号、工单类型和当前处理状态查询。列表显示工单编号、类型、标题、关联业务号、创建人、当前状态、首次响应时间和创建时间。
|
||||||
|
|
||||||
|
常见状态包括已分派、处理中、已解决和已完结;取消后的工单也会保留记录。
|
||||||
|
|
||||||
|
## 创建工单
|
||||||
|
|
||||||
|
1. 点击“创建工单”。
|
||||||
|
2. 选择工单类型,如卡问题、套餐问题、订单问题、系统问题或其他问题。
|
||||||
|
3. 填写关联业务 ID。卡问题建议填写完整 ICCID,并在问题描述中补充 MSISDN。
|
||||||
|
4. 填写清晰的工单标题和问题描述。
|
||||||
|
5. 按需上传截图、日志或结果文件。
|
||||||
|
6. 核对创建人信息后提交。
|
||||||
|
|
||||||
|
问题描述建议包含:发生时间、影响范围、卡号、设备所在地、预期结果、实际现象、已执行的同步或诊断操作。不要在附件中上传 API 密钥、账号密码等敏感信息。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 沟通与处理
|
||||||
|
|
||||||
|
点击工单右侧“沟通”;工单已结束时按钮显示为“查看”。工单问题沟通页会显示历史消息、附件、工单信息和处理轨迹。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- 在输入区填写回复内容,或按权限上传附件后发送。
|
||||||
|
- 平台回复后可继续补充信息,尽量在同一工单中保持上下文完整。
|
||||||
|
- 有转单权限时,可把工单转交其他处理方。
|
||||||
|
- 问题确认解决后可执行“解决”;不再需要处理时可“取消”。
|
||||||
|
- 工单完成后可“关闭”,并按 1 至 5 分提交满意度和评价内容。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
按钮是否出现取决于工单当前状态和用户权限。已完结或已取消的工单通常只能查看。
|
||||||
|
|
||||||
|
## 工单记录
|
||||||
|
|
||||||
|
**入口:** 左侧菜单 → **工单管理 → 工单记录**。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
该页面用于审计工单流转,可按工单编号和操作类型查询。主要字段包括操作类型、操作机构、操作用户、目标部门、目标用户、变更前状态和变更后状态。
|
||||||
|
|
||||||
|
常见操作类型为创建、分派、处理、完成、关单和取消。需要核对“谁在何时改变了工单状态”时,以此页面记录为准;如已授权,可查看详情或导出当前查询结果。
|
||||||
|
|
||||||
|
## 何时新建工单
|
||||||
|
|
||||||
|
- 同一张卡完成状态同步和智能诊断后仍无法联网。
|
||||||
|
- 批量任务在执行日志中失败,错误原因无法自行处理。
|
||||||
|
- 套餐、费用或归属与实际业务不符。
|
||||||
|
- 页面持续报错,且刷新或重新登录后仍可复现。
|
||||||
|
|
||||||
|
同一问题不要重复创建多个工单。补充信息应在原工单的沟通页继续发送。
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
layout: home
|
||||||
|
|
||||||
|
hero:
|
||||||
|
name: iotLCM OpenAPI
|
||||||
|
text: 接口文档
|
||||||
|
tagline: 对接说明:鉴权方式、请求参数、响应结构与示例。
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: V3 接口(推荐)
|
||||||
|
link: /openapi/v3/
|
||||||
|
- theme: alt
|
||||||
|
text: 旧版兼容接口
|
||||||
|
link: /openapi/legacy/
|
||||||
|
|
||||||
|
features:
|
||||||
|
- title: 版本选型指引
|
||||||
|
details: 新接入推荐使用 V3。V3 接口位于 /openapi/v3/query,通过 X-Access-Token 鉴权,调用时需在请求体中传入 token 接口返回的 appId(⚠️ 注意区分:appKey 用于获取 token,appId 由 token 接口返回并在后续调用中传入),数据权限按账号所属客户隔离;旧版兼容接口位于 /route/open,通过请求 body verify 入参鉴权,仅作为存量客户兼容保留。
|
||||||
|
- title: V3 接口
|
||||||
|
details: 鉴权、获取客户调用凭证、查询 SIM 卡信息、查询 SIM 流量限制、查询 SIM IMEI(单卡 / 批量)、查询 SIM 流量用量。
|
||||||
|
- title: 旧版兼容接口
|
||||||
|
details: 查询 SIM 详情、查询 SIM 账期用量、查询 SIM 历史月用量、查询 SIM 状态、查询 SIM 在线会话、查询卡列表信息。
|
||||||
|
---
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
title: 概览与鉴权
|
||||||
|
description: iotLCM 旧版兼容接口概览与鉴权说明
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 概览与鉴权
|
||||||
|
|
||||||
|
本文档收录历史 `/route/open/*` 路径下仍在维护的兼容接口。鉴权通过请求体 `verify` 中的 `appId`、`password` 和 `timeStamp` 完成;`sign` 为历史兼容字段,当前服务不校验。
|
||||||
|
|
||||||
|
这些接口由 iotLCM OpenAPI 服务实现并提供,与 V3 接口同域(`api.example.com`),仅请求路径与鉴权方式不同。
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
完整请求 URL:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com/route/open/{接口名}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 统一鉴权
|
||||||
|
|
||||||
|
以下各接口的“请求示例”均为完整 JSON 请求体,调用时请设置 `Content-Type: application/json`;`verify.sign` 可传空字符串 `""`。
|
||||||
|
|
||||||
|
所有接口通过请求体 `verify` 字段鉴权:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| appId | string | 用于唯一标识请求 |
|
||||||
|
| password | string | 密码 |
|
||||||
|
| timeStamp | string | 时间戳,`"" + System.currentTimeMillis()` |
|
||||||
|
| sign | string | 当前服务不校验,可传空字符串 `""`。 |
|
||||||
|
|
||||||
|
### 历史签名规则(仅供参考)
|
||||||
|
|
||||||
|
当前服务不要求计算签名,也无需提供 `accessKey`。以下为历史客户端示例中的签名算法,仅供维护已有签名逻辑时参考。
|
||||||
|
|
||||||
|
```text
|
||||||
|
appId={appId}&password={password}&timeStamp={timeStamp}&key={accessKey}
|
||||||
|
```
|
||||||
|
|
||||||
|
对拼接后的字符串计算 MD5,并将结果转为大写,即为 `sign`。`sign` 不参与签名计算;`accessKey` 仅用于签名,不放入请求体的 `verify` 字段。
|
||||||
|
|
||||||
|
## 统一返回结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| status | number | 业务状态码,成功为 `0` |
|
||||||
|
| message | string | 错误或提示信息 |
|
||||||
|
| result | any | 业务数据 |
|
||||||
|
|
||||||
|
## 公共字典
|
||||||
|
|
||||||
|
卡状态编码(适用于 `cardInfo`、`simStatus`、`queryCardInfoList` 响应中的 `statusId` / `statusCode` / `status`):
|
||||||
|
|
||||||
|
| 编码 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 1 | 库存 |
|
||||||
|
| 2 | 可测试 |
|
||||||
|
| 3 | 待激活 |
|
||||||
|
| 4 | 已激活 |
|
||||||
|
| 5 | 已停机 |
|
||||||
|
| 6 | 预销户 |
|
||||||
|
| 7 | 已销户 |
|
||||||
|
| 8 | 未知 |
|
||||||
|
|
||||||
|
## 请求案例
|
||||||
|
|
||||||
|
以下为历史 Java 示例,保留原有 `verify` 块与 `sign` 的生成方式供参考。调用当前服务时,可直接设置 `verify.sign` 为 `""`,省略签名计算。
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 服务器请求地址
|
||||||
|
protected static String server_Ip = "https://api.example.com/route/open";
|
||||||
|
// appId
|
||||||
|
public static String appId = null;
|
||||||
|
// 密钥密码
|
||||||
|
public static String password = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign 加密类
|
||||||
|
*/
|
||||||
|
public static String getSign(Map<String, Object> map, String key) {
|
||||||
|
List<String> list = new ArrayList<>(map.keySet());
|
||||||
|
Collections.sort(list);
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
String k = list.get(i);
|
||||||
|
String v = (String) map.get(k);
|
||||||
|
sb.append(k).append("=").append(v).append("&");
|
||||||
|
}
|
||||||
|
String signstr = sb.append("key=").append(key).toString();
|
||||||
|
String sign = MD5Util.MD5Encode(signstr).toUpperCase();
|
||||||
|
return sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
Map<String, Object> parmap = new HashMap<>();
|
||||||
|
Map<String, Object> verify = new HashMap<>();
|
||||||
|
|
||||||
|
String appId = "test";
|
||||||
|
String password = "uhbhkh223123";
|
||||||
|
String accessKey = "1234567896132";
|
||||||
|
String timeStamp = "" + System.currentTimeMillis();
|
||||||
|
|
||||||
|
verify.put("password", password);
|
||||||
|
verify.put("appId", appId);
|
||||||
|
verify.put("timeStamp", timeStamp);
|
||||||
|
verify.put("sign", getSign(verify, accessKey));
|
||||||
|
|
||||||
|
parmap.put("type", "iccid");
|
||||||
|
parmap.put("cardno", "89860400000000002135");
|
||||||
|
|
||||||
|
map.put("verify", verify);
|
||||||
|
map.put("Param", parmap);
|
||||||
|
|
||||||
|
server_Ip += "/cardInfo";
|
||||||
|
String result = HttpUtil.post(server_Ip, JSON.toJSONString(map));
|
||||||
|
System.out.println(result);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
---
|
||||||
|
title: 查询类
|
||||||
|
description: iotLCM 旧版兼容查询类接口
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 查询类
|
||||||
|
|
||||||
|
## 查询 SIM 详情
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/cardInfo
|
||||||
|
```
|
||||||
|
|
||||||
|
简要描述:查询物联卡基础信息。
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| type | string | 查询类型:`msisdn` / `iccid` / `imsi` / `vid` |
|
||||||
|
| cardno | string | 对应的查询值 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"type": "iccid",
|
||||||
|
"cardno": "89860400000000005474"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| vid | string | 本地卡记录 ID |
|
||||||
|
| msisdn | string | MSISDN |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
| imsi | string | IMSI |
|
||||||
|
| imei | string | IMEI |
|
||||||
|
| deptName | string | 当前归属组织名称 |
|
||||||
|
| statusId | number | 状态编码,参考公共字典 |
|
||||||
|
| packageId | string | 当前套餐编码 |
|
||||||
|
| packageName | string | 当前套餐名称 |
|
||||||
|
| used | number | 当前周期已用流量,单位 MB |
|
||||||
|
| remaining | number | 当前周期剩余流量,单位 MB |
|
||||||
|
| type | string | 卡类型名称 |
|
||||||
|
| packetArr | array | 资费计划,未配置时返回 `null` |
|
||||||
|
| remark | string | 备注 |
|
||||||
|
| grouping | string | 分组,未配置时为空字符串 |
|
||||||
|
| deliverDate | string | 发货日期,未配置时为空字符串 |
|
||||||
|
| activateDate | string | 激活日期 |
|
||||||
|
| expireTime | string | 到期时间 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {
|
||||||
|
"vid": "3933",
|
||||||
|
"msisdn": "1441543605474",
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"imsi": "460087436015474",
|
||||||
|
"imei": "",
|
||||||
|
"deptName": "示例企业",
|
||||||
|
"statusId": 4,
|
||||||
|
"packageId": "yd-100m-3y",
|
||||||
|
"packageName": "每月100m3年",
|
||||||
|
"used": 0.875,
|
||||||
|
"remaining": 99.125,
|
||||||
|
"type": "",
|
||||||
|
"packetArr": null,
|
||||||
|
"remark": "测试卡",
|
||||||
|
"grouping": "",
|
||||||
|
"deliverDate": "",
|
||||||
|
"activateDate": "2026-06-01",
|
||||||
|
"expireTime": "2029-01-31 23:59:59"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询 SIM 账期用量
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/simDataUsage
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| useUnit | string | 单位,当前为 `MB` |
|
||||||
|
| useAmount | number | 当前周期已用流量 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 0.875
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询 SIM 历史月用量
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/simDataUsageMonthly
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
| dateTime | string | 查询月份,格式 `yyyyMM`(示例:`202509`) |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"dateTime": "202509"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| useUnit | string | 单位,当前为 `MB` |
|
||||||
|
| useAmount | number | 指定月份用量 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询 SIM 状态
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/simStatus
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| statusMessage | string | 状态描述 |
|
||||||
|
| statusCode | number | 状态编码,参考公共字典 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {
|
||||||
|
"statusMessage": "已激活",
|
||||||
|
"statusCode": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询 SIM 在线会话
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/simSession
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| typeName | string | 运营商系统类型,未上报时为空字符串 |
|
||||||
|
| imei | string | IMEI,未上报时为空字符串 |
|
||||||
|
| statusMessage | string | 状态描述,未上报时为空字符串 |
|
||||||
|
| statusCode | number | 状态码,未上报时为 `0` |
|
||||||
|
| data | object | 会话详情 |
|
||||||
|
|
||||||
|
`data` 字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| apnId | string | APN ID |
|
||||||
|
| rat | string | 接入方式 |
|
||||||
|
| ipv6 | string | IPv6 地址 |
|
||||||
|
| ip | string | IPv4 地址 |
|
||||||
|
| ipv6Prefix | string | IPv6 前缀 |
|
||||||
|
| status | string | 在线状态 |
|
||||||
|
| createDate | string | 会话创建时间 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": {
|
||||||
|
"typeName": "",
|
||||||
|
"imei": "",
|
||||||
|
"statusMessage": "",
|
||||||
|
"statusCode": 0,
|
||||||
|
"data": {
|
||||||
|
"apnId": "",
|
||||||
|
"rat": "",
|
||||||
|
"ipv6": "",
|
||||||
|
"ip": "",
|
||||||
|
"ipv6Prefix": "",
|
||||||
|
"status": "",
|
||||||
|
"createDate": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询卡列表信息
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /route/open/queryCardInfoList
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | `array<string>` | ICCID 列表,最多支持 100 个 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Param": {
|
||||||
|
"iccid": [
|
||||||
|
"89860400000000005473",
|
||||||
|
"89860400000000005474"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"timeStamp": "1784097284347",
|
||||||
|
"password": "example-password",
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"sign": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
| activateDate | string | 激活日期 |
|
||||||
|
| dueExpireTime | string | 到期时间 |
|
||||||
|
| imei | string | IMEI |
|
||||||
|
| imsi | string | IMSI |
|
||||||
|
| msisdn | string | MSISDN |
|
||||||
|
| type | string | 卡类型 |
|
||||||
|
| remarks | string | 备注 |
|
||||||
|
| status | number | 状态编码,参考公共字典 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"message": "",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005473",
|
||||||
|
"activateDate": "2023-05-11",
|
||||||
|
"dueExpireTime": "",
|
||||||
|
"imei": "",
|
||||||
|
"imsi": "460087436015473",
|
||||||
|
"msisdn": "1441543605473",
|
||||||
|
"type": "",
|
||||||
|
"remarks": "测试卡",
|
||||||
|
"status": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"activateDate": "2026-06-01",
|
||||||
|
"dueExpireTime": "2029-01-31 23:59:59",
|
||||||
|
"imei": "",
|
||||||
|
"imsi": "460087436015474",
|
||||||
|
"msisdn": "1441543605474",
|
||||||
|
"type": "",
|
||||||
|
"remarks": "测试卡",
|
||||||
|
"status": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: 共享 Token 接口概览
|
||||||
|
description: 共享 Token 接口与白名单转发说明
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 共享 Token 接口概览
|
||||||
|
|
||||||
|
本目录包含共享 Token 获取接口和白名单转发说明。
|
||||||
|
|
||||||
|
## 接口文档
|
||||||
|
|
||||||
|
- [共享 Token 接口](./shared-token/)
|
||||||
|
- [白名单转发](./mobile-v5/)
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
title: 白名单转发
|
||||||
|
description: 通过 api.example.com 访问中国移动物联网能力开放平台 V5 接口
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 白名单转发
|
||||||
|
|
||||||
|
本目录说明如何通过 `api.example.com` 访问中国移动物联网能力开放平台 V5 接口。平台仅提供安全代理转发,不改变中国移动 V5 接口的请求方法、请求参数、响应结构和业务语义。
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
客户侧统一使用:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
V5 接口的请求路径保持与中国移动官方接口一致,将官方域名替换为平台域名即可:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com/v5/{官方接口路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
例如,官方接口路径为 `/v5/ec/query/sim-basic-info` 时,客户侧请求路径为:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST https://api.example.com/v5/ec/query/sim-basic-info
|
||||||
|
```
|
||||||
|
|
||||||
|
平台会将该请求转发至:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.iot.10086.cn/v5/ec/query/sim-basic-info
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT] 使用 POST 时务必携带 JSON 请求体
|
||||||
|
> 中国移动 V5 接口对 `POST` 只接受 JSON 格式。通过代理请求时必须设置
|
||||||
|
> `Content-Type: application/json`,并在请求体中传入参数;
|
||||||
|
> 未带该请求头或使用表单编码(`application/x-www-form-urlencoded`)会返回
|
||||||
|
> `{"status":"11012","message":"POST请求仅支持JSON格式","result":[]}`。
|
||||||
|
>
|
||||||
|
> 若使用 `GET`,则以 Query 参数传参,无需设置 `Content-Type`。
|
||||||
|
|
||||||
|
## 调用前准备
|
||||||
|
|
||||||
|
调用 V5 业务接口前,需要先从平台取得以下信息:
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `appid` | 第三方应用唯一标识 |
|
||||||
|
| `password` | 应用密钥 |
|
||||||
|
| `transid` | 按官方规则生成的事务编码 |
|
||||||
|
| `token` | 通过共享 Token 接口获取的运营商令牌 |
|
||||||
|
|
||||||
|
其中 `transid` 由客户生成,通常遵循 `appid + YYYYMMDDHHMISS + 8位数字序列`;中国移动通常不校验此事务编号。具体规则及字段约束以中国移动当前发布的接口文档为准。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 业务接口示例
|
||||||
|
|
||||||
|
以下接口字段摘自中国移动 V5 接口清单,用于说明代理路径的写法。完整接口、字段、错误码和版本变更请以官方文档为准。
|
||||||
|
|
||||||
|
### 单卡基本信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST https://api.example.com/v5/ec/query/sim-basic-info
|
||||||
|
```
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `transid` | string | 事务编码 |
|
||||||
|
| `token` | string | V5 Token |
|
||||||
|
| `msisdn` | string | 物联卡号码 |
|
||||||
|
| `iccid` | string | ICCID;与 `msisdn`、`imsi` 按官方规则选择 |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"transid": "113712264334542026051515200001000",
|
||||||
|
"token": "example-access-token",
|
||||||
|
"iccid": "89860800000000000009"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应主要字段:
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `status` | string | `0` 表示成功,非 `0` 表示失败 |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `result` | array | 查询结果 |
|
||||||
|
| `result[].msisdn` | string | 物联卡号码 |
|
||||||
|
| `result[].iccid` | string | ICCID |
|
||||||
|
| `result[].imsi` | string | IMSI |
|
||||||
|
| `result[].activeDate` | string | 首次激活日期 |
|
||||||
|
| `result[].openDate` | string | 开卡时间 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "0",
|
||||||
|
"message": "正确",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"msisdn": "1442206200009",
|
||||||
|
"imsi": "460240406200009",
|
||||||
|
"iccid": "89860800000000000009",
|
||||||
|
"activeDate": "2025-03-26 15:49:50",
|
||||||
|
"openDate": "2025-03-13 16:52:10",
|
||||||
|
"remark": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 请求与响应约定
|
||||||
|
|
||||||
|
- V5 接口支持的请求方法、参数位置(Query 或 Form)、参数格式和字段大小写均以中国移动官方文档为准。
|
||||||
|
- `GET` 与 `POST` 均可通过本代理访问。使用 `POST` 时**必须**设置 `Content-Type: application/json` 并携带 JSON 请求体,否则返回 `11012`;使用表单编码亦不被接受。
|
||||||
|
- 代理会以平台固定出口地址访问中国移动,因此客户端侧的来源 IP 无需加入中国移动白名单;请勿在请求中自行添加 `X-Forwarded-For`、`X-Real-IP` 等来源标识请求头,中国移动会依据其判定 IP 鉴权,携带非白名单地址将返回 `12003`。
|
||||||
|
- 平台不将 V5 响应包装成 iotLCM V3 的 `code/message/data` 结构,客户应直接按照中国移动 V5 响应结构解析。
|
||||||
|
- 运营商 Token 具有有效期,收到 Token 失效类错误后应重新获取,不建议长期缓存。
|
||||||
|
|
||||||
|
## 官方资料
|
||||||
|
|
||||||
|
接口完整清单、参数定义、返回示例、错误码和 SDK 示例请参阅:
|
||||||
|
|
||||||
|
[中国移动物联网能力开放平台 V5 接口文档下载页面](https://api.iot.10086.cn/api/index.html#/resourceCenter/docDownload)
|
||||||
|
|
||||||
|
平台代理入口仅替换访问域名,不替代中国移动官方接口规范。
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
title: 共享 Token 接口
|
||||||
|
description: 共享 Token 接口说明
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 共享 Token 接口
|
||||||
|
|
||||||
|
本接口用于获取平台已开放共享权限的移动运营商访问 Token。客户无需直接维护运营商应用密钥,平台根据客户已授权的通道配置完成校验并向运营商申请 Token。
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP(`http://api.example.com`)仅为存量兼容保留,不做自动跳转,新接入请使用 HTTPS。
|
||||||
|
|
||||||
|
## 接口信息
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/operator-token
|
||||||
|
```
|
||||||
|
|
||||||
|
请求头:`Content-Type: application/json`。
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 平台分配的客户应用标识 |
|
||||||
|
| `chanKey` | string | 已开通共享 Token 权限的通道标识 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"chanKey": "channel_010"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应中的 `data.accessToken` 用于调用移动运营商 V5 接口。Token 的有效期以 `data.tokenExpire` 为准,请勿将 Token 写入代码仓库或日志。
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
失败时,接口返回的 `data` 为 `{}`,不是 `null`;例如 `code=50` 表示平台侧获取运营商 Token 失败。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"chanAlias": "中国移动物联网",
|
||||||
|
"chanKey": "channel_010",
|
||||||
|
"accessToken": "example-access-token",
|
||||||
|
"tokenExpire": "2026-09-09 16:00:00"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 平台业务状态码,`0` 表示成功;`50` 表示平台侧获取运营商 Token 失败 |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data` | object | 成功时返回 Token 信息;失败时返回空对象 `{}`,不是 `null` |
|
||||||
|
| `data.chanAlias` | string | 通道名称 |
|
||||||
|
| `data.chanKey` | string | 通道标识 |
|
||||||
|
| `data.accessToken` | string | 移动运营商接口访问 Token |
|
||||||
|
| `data.tokenExpire` | string | Token 过期时间 |
|
||||||
|
| `error` | any | 错误详情;无错误时通常为 `null` |
|
||||||
|
|
||||||
|
## 调用示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --request POST \
|
||||||
|
--url https://api.example.com/openapi/v3/query/operator-token \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"appId": "example-app-id",
|
||||||
|
"chanKey": "channel_010"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
共享 Token 仅适用于已开通共享权限的通道。若通道未授权、客户应用标识与通道不匹配,或运营商 Token 获取失败,接口将返回失败信息。
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: 共享 Token 接口
|
||||||
|
description: 共享 Token 接口文档兼容入口
|
||||||
|
sidebar: false
|
||||||
|
search: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# 共享 Token 接口
|
||||||
|
|
||||||
|
文档已归档到运营商接口模块,请访问[共享 Token 接口](./operator/shared-token/)。
|
||||||
@@ -0,0 +1,352 @@
|
|||||||
|
---
|
||||||
|
title: 业务类
|
||||||
|
description: iotLCM V3 业务类接口
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 业务类
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP(`http://api.example.com`)仅为存量兼容保留,不做自动跳转,新接入请使用 HTTPS。
|
||||||
|
|
||||||
|
## 鉴权
|
||||||
|
|
||||||
|
先调用 `/openapi/v3/query/token` 获取访问令牌,再在请求头中传入 `X-Access-Token`。请求体中的 `appId` 必须与凭证所属客户一致。
|
||||||
|
|
||||||
|
## 标准入口与兼容入口
|
||||||
|
|
||||||
|
新接入统一使用本文的 `/openapi/v3/change/*` 路径。原 `/openapi/v3/common/*` 前缀已停止提供,调用返回 `404`,请改用 `/change` 下的对应接口。
|
||||||
|
|
||||||
|
批量续费使用 `/change/batch-sim-subscribe`;历史 `/change/renew` 接收 `cards[]`,允许每张卡携带套餐信息,作为兼容入口保留。两者共用续费实现。
|
||||||
|
|
||||||
|
批量变更达量断网使用 `/change/batch-sim-traffic-cutoff`。原 `/change/traffic-cutoff-config` 不再对外提供,请改用标准入口——两者请求参数完全一致,迁移无需改动报文体。`/query/traffic-cutoff-config` 仅查询配置。
|
||||||
|
|
||||||
|
## 统一返回结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 业务状态码,成功为 `0` |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data` | object | 业务数据 |
|
||||||
|
| `error` | any | 错误详情 |
|
||||||
|
|
||||||
|
## 单卡套餐续费
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/change/sim-subscribe
|
||||||
|
```
|
||||||
|
|
||||||
|
单卡续费按串行方式执行,接口等待本卡处理完成后直接返回结果。
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `iccid` | string | ICCID |
|
||||||
|
| `msisdn` | string | MSISDN/卡号 |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
| `packageId` | string | 目标套餐编码 |
|
||||||
|
| `validity` | integer | 续费时长,单位为年,最小值为 `1` |
|
||||||
|
| `remark` | string | 续费备注 |
|
||||||
|
|
||||||
|
> `iccid`、`msisdn`、`imsi` 至少传一个。
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202608-0001",
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"packageId": "001",
|
||||||
|
"validity": 1,
|
||||||
|
"remark": "客户单卡续费"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `orderId` | string | 订单 ID |
|
||||||
|
| `orderSn` | string | 订单号 |
|
||||||
|
| `iccid` | string | 续费卡 ICCID |
|
||||||
|
| `planName` | string | 续费套餐名称 |
|
||||||
|
| `effectiveType` | string | 生效类型 |
|
||||||
|
| `effectiveDate` | string | 套餐生效日期 |
|
||||||
|
| `expireDate` | string | 套餐到期日期 |
|
||||||
|
| `amount` | number | 续费金额 |
|
||||||
|
| `status` | string | 续费状态,成功为 `success`,失败为 `failed` |
|
||||||
|
| `errorCode` | string | 失败时返回的错误编码 |
|
||||||
|
| `errorMessage` | string | 失败时返回的错误说明 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"orderId": "54",
|
||||||
|
"orderSn": "REN20260910153546001000001",
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"planName": "中移-1G",
|
||||||
|
"effectiveType": "immediate",
|
||||||
|
"effectiveDate": "2026-09-10 15:35:46",
|
||||||
|
"expireDate": "2027-09-10 15:35:46",
|
||||||
|
"amount": 0,
|
||||||
|
"status": "success"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
失败时仍按统一返回结构返回 `data`,并通过 `status=failed` 和 `errorMessage` 说明失败原因。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"orderId": "0",
|
||||||
|
"orderSn": "",
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"planName": "",
|
||||||
|
"effectiveType": "",
|
||||||
|
"effectiveDate": "",
|
||||||
|
"expireDate": "",
|
||||||
|
"amount": 0,
|
||||||
|
"status": "failed",
|
||||||
|
"errorCode": "RENEW_FAILED",
|
||||||
|
"errorMessage": "卡片不存在或无权限"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 单卡限额变更
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/change/sim-traffic-cutoff
|
||||||
|
```
|
||||||
|
|
||||||
|
单卡配置按串行方式执行,接口等待本卡处理完成后直接返回配置结果。
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `iccid` | string | ICCID |
|
||||||
|
| `msisdn` | string | MSISDN/卡号 |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
| `trafficCutoffStatus` | integer | `0` 删除,`1` 开启,`2` 关闭 |
|
||||||
|
| `trafficCutoffThreshold` | number | 达量阈值,单位为 MB;开启时必须大于 `0` |
|
||||||
|
| `remark` | string | 配置备注 |
|
||||||
|
|
||||||
|
> `iccid`、`msisdn`、`imsi` 三码至少传一个。trafficCutoffStatus=0 表示取消达量断网配置。
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202608-0001",
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffThreshold": 1024,
|
||||||
|
"remark": "客户开启达量断网"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `success` | boolean | 是否配置成功 |
|
||||||
|
| `iccid` | string | 平台卡 ICCID |
|
||||||
|
| `msisdn` | string | MSISDN |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
| `trafficCutoffStatus` | integer | `0` 删除,`1` 已开启,`2` 已关闭 |
|
||||||
|
| `trafficCutoffThreshold` | number | 达量阈值,单位为 MB |
|
||||||
|
| `trafficCutoffThresholdUnit` | string | 阈值单位,固定为 `MB` |
|
||||||
|
| `message` | string | 处理结果说明 |
|
||||||
|
| `errorCode` | string | 失败时的错误编码 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"success": true,
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"msisdn": "14100005152",
|
||||||
|
"imsi": "460045462635152",
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffThreshold": 1024,
|
||||||
|
"trafficCutoffThresholdUnit": "MB",
|
||||||
|
"message": "traffic cutoff configuration succeeded"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
失败时仍返回同一响应结构,`success=false`,`message` 为失败原因。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"success": false,
|
||||||
|
"iccid": "89860400000000005152",
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffThreshold": 1024,
|
||||||
|
"trafficCutoffThresholdUnit": "MB",
|
||||||
|
"message": "卡片不存在或无权限",
|
||||||
|
"errorCode": "TRAFFIC_CUTOFF_FAILED"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量套餐续费
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/change/batch-sim-subscribe
|
||||||
|
```
|
||||||
|
|
||||||
|
批量续费只创建异步批次,不等待所有卡执行完成。请保存响应中的 `batchNo`,再通过批次查询接口跟踪处理结果。
|
||||||
|
|
||||||
|
批量接口提交成功后只返回批次信息;卡片执行成功或失败请通过批次查询接口获取。
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `cards` | array | 续费卡列表 |
|
||||||
|
| `cards[].iccid` | string | ICCID |
|
||||||
|
| `cards[].msisdn` | string | MSISDN/卡号 |
|
||||||
|
| `cards[].imsi` | string | IMSI |
|
||||||
|
| `packageId` | string | 目标套餐编码 |
|
||||||
|
| `validity` | integer | 续费时长,单位为年 |
|
||||||
|
| `remark` | string | 续费备注 |
|
||||||
|
|
||||||
|
> 每个 `cards[]` 明细中的`iccid`、`msisdn`、`imsi` 三码至少传一个。
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202608-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005152"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageId": "001",
|
||||||
|
"validity": 1,
|
||||||
|
"remark": "客户批量续费"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `batchNo` | string | 系统生成的续费批次号 |
|
||||||
|
| `batchType` | integer | 批次类型,续费为 `101` |
|
||||||
|
| `total` | integer | 提交的卡数量 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"batchNo": "REN20260910153546001000002",
|
||||||
|
"batchType": 101,
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量限额变更
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/change/batch-sim-traffic-cutoff
|
||||||
|
```
|
||||||
|
|
||||||
|
批量配置只创建异步批次,不等待所有卡执行完成。请保存响应中的 `batchNo`,再通过批次查询接口跟踪处理结果。
|
||||||
|
|
||||||
|
批量接口提交成功后只返回批次信息;卡片执行成功或失败请通过批次查询接口获取。
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `cards` | array | 配置卡列表,单次最多 1000 张 |
|
||||||
|
| `cards[].iccid` | string | ICCID |
|
||||||
|
| `cards[].msisdn` | string | MSISDN/卡号 |
|
||||||
|
| `cards[].imsi` | string | IMSI |
|
||||||
|
| `trafficCutoffStatus` | integer | `0` 删除,`1` 开启,`2` 关闭 |
|
||||||
|
| `trafficCutoffThreshold` | number | 达量阈值,单位为 MB;开启时必须大于 `0` |
|
||||||
|
| `remark` | string | 配置批次备注 |
|
||||||
|
|
||||||
|
> 每个 `cards[]` 明细中的`iccid`、`msisdn`、`imsi` 三码至少传一个。`trafficCutoffStatus=0`时表示取消达量断网配置。
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202608-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005152"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffThreshold": 1024,
|
||||||
|
"remark": "客户批量开启达量断网"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `batchNo` | string | 系统生成的达量断网配置批次号 |
|
||||||
|
| `batchType` | integer | 批次类型,达量断网配置为 `41` |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"batchNo": "TCC20260910153546001000001",
|
||||||
|
"batchType": 41
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
title: 概览与鉴权
|
||||||
|
description: iotLCM V3 接口概览与鉴权说明
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 概览与鉴权
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP(`http://api.example.com`)仅为存量兼容保留,不做自动跳转,新接入请使用 HTTPS。
|
||||||
|
|
||||||
|
## 统一返回结构
|
||||||
|
|
||||||
|
V3 返回格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| code | number | 业务状态码,成功为 `0` |
|
||||||
|
| message | string | 错误或提示信息 |
|
||||||
|
| data | object | 业务数据 |
|
||||||
|
| error | any | 错误信息字段 |
|
||||||
|
|
||||||
|
## 公共字典
|
||||||
|
|
||||||
|
卡状态编码
|
||||||
|
|
||||||
|
| 编码 | 说明 |
|
||||||
|
| :-: | :-: |
|
||||||
|
| 0 | 未知 |
|
||||||
|
| 1 | 待激活 |
|
||||||
|
| 2 | 已激活 |
|
||||||
|
| 4 | 停机 |
|
||||||
|
| 6 | 可测试 |
|
||||||
|
| 7 | 库存 |
|
||||||
|
| 8 | 预销户 |
|
||||||
|
| 9 | 已销户 |
|
||||||
|
|
||||||
|
## V3 鉴权
|
||||||
|
|
||||||
|
V3 路由前缀:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/openapi/v3/query
|
||||||
|
/openapi/v3/change
|
||||||
|
```
|
||||||
|
|
||||||
|
文档中的“请求示例”仅展示请求 body;调用 V3 POST 接口时,请设置 `Content-Type: application/json` 并将示例 JSON 作为请求体发送。请求路径和鉴权请求头见各接口说明。
|
||||||
|
|
||||||
|
除获取 token 接口外,其他 V3 接口需要请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
也支持传入带 `Bearer ` 前缀的值,服务端会去掉前缀。
|
||||||
|
|
||||||
|
::: info 重要:请求体中的 appId 必须正确
|
||||||
|
先调用 `/openapi/v3/query/token` 获取 token,接口响应中会返回 `data.appId`(客户组织编码,格式如 `CUS-202601-0001`)。**后续调用所有 V3 接口时,请求体都必须传入这个 `appId` 值**,且要与 token 所属的客户组织匹配,否则返回 `code: 61 "登录已过期"`。
|
||||||
|
|
||||||
|
⚠️ **不要把 `appKey`(API 账号标识)当成 `appId` 使用。** `appKey` 只用于获取 token。
|
||||||
|
:::
|
||||||
|
|
||||||
|
权限控制:调用接口需具备对应接口资源的访问权限,权限与 API 账号所属权限组关联。未登记资源、资源停用、账号未授权均拒绝访问,返回 `10014`;权限数据查询异常返回 `10016`(系统侧故障,不是授权问题)。
|
||||||
|
|
||||||
|
原 `/openapi/v3/common/*` 前缀已停止提供,调用返回 `404`,请使用 `/openapi/v3/change/*` 下的对应接口。
|
||||||
@@ -0,0 +1,783 @@
|
|||||||
|
---
|
||||||
|
title: 查询类
|
||||||
|
description: iotLCM V3 查询类接口
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 查询类
|
||||||
|
|
||||||
|
## 单卡基本信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-info
|
||||||
|
```
|
||||||
|
|
||||||
|
请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| appId | string | 客户组织编码 |
|
||||||
|
| iccid | string | ICCID,`iccid`、`msisdn`、`imsi` 至少传一个 |
|
||||||
|
| msisdn | string | MSISDN |
|
||||||
|
| imsi | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.iccid` | string | ICCID |
|
||||||
|
| `data.msisdn` | string | MSISDN |
|
||||||
|
| `data.imsi` | string | IMSI |
|
||||||
|
| `data.imei` | string | IMEI |
|
||||||
|
| `data.username` | string | 当前归属客户/组织名称 |
|
||||||
|
| `data.location` | string | 归属地 |
|
||||||
|
| `data.planCategory` | string | 套餐分类 |
|
||||||
|
| `data.currentPlanName` | string | 当前套餐名称 |
|
||||||
|
| `data.plangroup` | string | 套餐分组,未配置时为空字符串 |
|
||||||
|
| `data.planid` | string | 当前套餐编码 |
|
||||||
|
| `data.appid` | string | 归属客户组织编码 |
|
||||||
|
| `data.allowPlanChange` | number | 是否允许改套餐,未启用时返回 `0` |
|
||||||
|
| `data.lastSyncTime` | string | 状态最后同步时间 |
|
||||||
|
| `data.accessType` | string | 接入方式 |
|
||||||
|
| `data.cardStatus` | string | 卡状态 |
|
||||||
|
| `data.cardType` | any | 卡类型 |
|
||||||
|
| `data.networkType` | any | 网络类型 |
|
||||||
|
| `data.activationDate` | string | 出库/激活相关日期 |
|
||||||
|
| `data.activeDate` | string | 当前套餐生效日期 |
|
||||||
|
| `data.validityPeriod` | number | 有效期,按月份计算 |
|
||||||
|
| `data.expiryDate` | string | 当前套餐到期日期 |
|
||||||
|
| `data.testPeriod` | number | 测试期,未启用时返回 `0` |
|
||||||
|
| `data.testPeriodExpiryDate` | any | 测试期到期时间,未启用时返回 `null` |
|
||||||
|
| `data.silentPeriod` | number | 沉默期,未启用时返回 `0` |
|
||||||
|
| `data.silentPeriodExpiryDate` | any | 沉默期到期时间,未启用时返回 `null` |
|
||||||
|
| `data.dataService` | number | 数据服务状态,`1` 启用,`0` 禁用 |
|
||||||
|
| `data.planData` | number | 套餐总流量,单位 MB |
|
||||||
|
| `data.usedData` | number | 已用流量,单位 MB |
|
||||||
|
| `data.remainingData` | string | 剩余流量,单位 MB |
|
||||||
|
| `data.smsService` | number | 短信服务状态 |
|
||||||
|
| `data.planSMS` | number | 套餐短信总量 |
|
||||||
|
| `data.usedSMS` | number | 已用短信数 |
|
||||||
|
| `data.remainingSMS` | string | 剩余短信数 |
|
||||||
|
| `data.voiceService` | number | 语音服务状态 |
|
||||||
|
| `data.planMinutes` | number | 套餐语音分钟数 |
|
||||||
|
| `data.usedMinutes` | number | 已用语音分钟数 |
|
||||||
|
| `data.remainingMinutes` | string | 剩余语音分钟数 |
|
||||||
|
| `data.internationalRoamingService` | number | 国际漫游服务状态 |
|
||||||
|
| `data.planInternationalData` | number | 国际漫游套餐流量 |
|
||||||
|
| `data.usedInternationalData` | number | 已用国际漫游流量 |
|
||||||
|
| `data.remainingInternationalData` | string | 剩余国际漫游流量 |
|
||||||
|
| `data.description` | any | 备注描述,未填写时返回 `null` |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"imei": "",
|
||||||
|
"username": "示例企业",
|
||||||
|
"location": "370100000000",
|
||||||
|
"planCategory": "移动小流量",
|
||||||
|
"currentPlanName": "每月100m3年",
|
||||||
|
"plangroup": "",
|
||||||
|
"planid": "yd-100m-3y",
|
||||||
|
"appid": "CUS-202601-0001",
|
||||||
|
"allowPlanChange": 0,
|
||||||
|
"lastSyncTime": "2026-06-24 00:03:19",
|
||||||
|
"accessType": "",
|
||||||
|
"cardStatus": "已激活",
|
||||||
|
"cardType": "",
|
||||||
|
"networkType": "",
|
||||||
|
"activationDate": "2026-06-01",
|
||||||
|
"activeDate": "2026-06-01",
|
||||||
|
"validityPeriod": 32,
|
||||||
|
"expiryDate": "2029-01-31",
|
||||||
|
"testPeriod": 0,
|
||||||
|
"testPeriodExpiryDate": null,
|
||||||
|
"silentPeriod": 0,
|
||||||
|
"silentPeriodExpiryDate": null,
|
||||||
|
"dataService": 1,
|
||||||
|
"planData": 100,
|
||||||
|
"usedData": 0.875,
|
||||||
|
"remainingData": "99.1",
|
||||||
|
"smsService": 1,
|
||||||
|
"planSMS": 0,
|
||||||
|
"usedSMS": 0,
|
||||||
|
"remainingSMS": "0",
|
||||||
|
"voiceService": 1,
|
||||||
|
"planMinutes": 0,
|
||||||
|
"usedMinutes": 0,
|
||||||
|
"remainingMinutes": "0",
|
||||||
|
"internationalRoamingService": 0,
|
||||||
|
"planInternationalData": 0,
|
||||||
|
"usedInternationalData": 0,
|
||||||
|
"remainingInternationalData": "0.0",
|
||||||
|
"description": null
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 单卡状态信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-status
|
||||||
|
```
|
||||||
|
|
||||||
|
请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | token 返回的客户组织编码 |
|
||||||
|
| `iccid` | string | ICCID;与 `msisdn`、`imsi` 至少传一个 |
|
||||||
|
| `msisdn` | string | MSISDN |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 业务状态码,成功为 `0` |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data.iccid` | string | ICCID,查询命中时返回 |
|
||||||
|
| `data.lifeCycle` | number | 卡生命周期状态编码 |
|
||||||
|
| `data.lifeCycleName` | string | 卡生命周期状态文字 |
|
||||||
|
| `error` | any | 错误信息字段 |
|
||||||
|
|
||||||
|
响应示例(查询命中):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"lifeCycle": 2,
|
||||||
|
"lifeCycleName": "已激活"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例(未命中):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 50,
|
||||||
|
"message": "查询sim卡信息失败,请确认ICCID,MSISDN,IMSI是否正确",
|
||||||
|
"data": {},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`lifeCycleName` 为状态文字;`lifeCycle` 是此接口的状态编码,不要与其他接口或字典中的状态编码混用。
|
||||||
|
|
||||||
|
## 单卡流量限额查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-limit
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `iccid` | string | ICCID |
|
||||||
|
| `msisdn` | string | MSISDN |
|
||||||
|
| `imsi` | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.trafficCutoffStatus` | integer | 达量断网状态:`0` 未配置、`1` 已开启、`2` 已关闭 |
|
||||||
|
| `data.trafficCutoffName` | string | 状态名称,返回 `未配置`、`已开启` 或 `已关闭` |
|
||||||
|
| `data.trafficCutoffThreshold` | number | 达量断网阈值,单位 MB |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffName": "已开启",
|
||||||
|
"trafficCutoffThreshold": 1024
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 单卡实时终端 IMEI 查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-imei
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| appId | string | 客户组织编码 |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
| msisdn | string | MSISDN |
|
||||||
|
| imsi | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.imei` | string | IMEI |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"imei": ""
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 单卡本月流量使用量查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-usage
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| appId | string | 客户组织编码 |
|
||||||
|
| iccid | string | ICCID |
|
||||||
|
| msisdn | string | MSISDN |
|
||||||
|
| imsi | string | IMSI |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.useUnit` | string | 流量单位,固定为 `MB` |
|
||||||
|
| `data.useAmount` | number | 当前周期已用流量 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 0.875
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量状态信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-status-batch
|
||||||
|
```
|
||||||
|
|
||||||
|
单次最多查询 1000 张卡。每张卡至少提供 `iccid`、`msisdn`、`imsi` 中的一个;同一条记录提供多个标识时,必须对应同一张卡。
|
||||||
|
|
||||||
|
请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | token 返回的客户组织编码,示例为 `CUS-202601-0001` |
|
||||||
|
| `cards` | array | 卡片标识列表;示例包含 3 张卡,单次最多 1000 项 |
|
||||||
|
| `cards[].iccid` | string | ICCID;示例中每项均使用 ICCID,实际调用时与 `msisdn`、`imsi` 至少传一个 |
|
||||||
|
| `cards[].msisdn` | string | MSISDN,可代替 ICCID 标识卡片 |
|
||||||
|
| `cards[].imsi` | string | IMSI,可代替 ICCID 标识卡片 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005473"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "8986110000000004371"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 业务状态码,成功为 `0` |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data.total` | integer | 本次请求的卡片总数;示例为 `3` |
|
||||||
|
| `data.matchedCount` | integer | 匹配成功的卡片数量;示例为 `3` |
|
||||||
|
| `data.notFoundCount` | integer | 未匹配成功的卡片数量;示例为 `0` |
|
||||||
|
| `data.items` | array | 每张卡的查询结果;示例依次对应请求中的 3 张卡 |
|
||||||
|
| `data.items[].matched` | boolean | 是否匹配成功;示例中均为 `true` |
|
||||||
|
| `data.items[].iccid` | string | 匹配成功时返回的 ICCID |
|
||||||
|
| `data.items[].lifeCycle` | number | 匹配成功时返回的卡生命周期状态编码;示例中 `2` 为已激活 |
|
||||||
|
| `data.items[].lifeCycleName` | string | 匹配成功时返回的卡生命周期状态文字 |
|
||||||
|
| `data.items[].errorCode` | string | 匹配失败时返回的错误码;示例均匹配成功,因此未返回 |
|
||||||
|
| `data.items[].errorMessage` | string | 匹配失败时返回的错误说明;示例均匹配成功,因此未返回 |
|
||||||
|
| `error` | any | 错误信息字段 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"total": 3,
|
||||||
|
"matchedCount": 3,
|
||||||
|
"notFoundCount": 0,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005473",
|
||||||
|
"lifeCycle": 2,
|
||||||
|
"lifeCycleName": "已激活"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"lifeCycle": 2,
|
||||||
|
"lifeCycleName": "已激活"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "8986110000000004371",
|
||||||
|
"lifeCycle": 2,
|
||||||
|
"lifeCycleName": "已激活"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量流量信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-usage-batch
|
||||||
|
```
|
||||||
|
|
||||||
|
单次最多查询 1000 张卡。请求体需传入 `appId` 和 `cards` 数组;数组中的每张卡至少提供 `iccid`、`msisdn`、`imsi` 中的一个。同一条记录提供多个标识时,必须对应同一张卡。
|
||||||
|
|
||||||
|
请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | token 返回的客户组织编码 |
|
||||||
|
| `cards` | array | 卡片标识列表,单次最多 1000 项 |
|
||||||
|
| `cards[].iccid` | string | ICCID;每项与 `msisdn`、`imsi` 至少传一个 |
|
||||||
|
| `cards[].msisdn` | string | MSISDN,可代替 ICCID 标识卡片 |
|
||||||
|
| `cards[].imsi` | string | IMSI,可代替 ICCID 标识卡片 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005473"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "8986110000000004371"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构(流量单位为 MB):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 业务状态码,成功为 `0` |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data.total` | integer | 本次请求的卡片总数 |
|
||||||
|
| `data.matchedCount` | integer | 匹配成功的卡片数量 |
|
||||||
|
| `data.notFoundCount` | integer | 未匹配成功的卡片数量 |
|
||||||
|
| `data.items` | array | 每张卡的查询结果 |
|
||||||
|
| `data.items[].matched` | boolean | 是否匹配成功 |
|
||||||
|
| `data.items[].iccid` | string | 匹配成功时返回的 ICCID |
|
||||||
|
| `data.items[].useUnit` | string | 流量单位,示例为 `MB` |
|
||||||
|
| `data.items[].useAmount` | number | 流量使用量 |
|
||||||
|
| `data.items[].errorCode` | string | 匹配失败时返回的错误码;匹配成功时不返回 |
|
||||||
|
| `data.items[].errorMessage` | string | 匹配失败时返回的错误说明;匹配成功时不返回 |
|
||||||
|
| `error` | any | 错误信息字段 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"total": 3,
|
||||||
|
"matchedCount": 3,
|
||||||
|
"notFoundCount": 0,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 0.875
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005473",
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 12.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "8986110000000004371",
|
||||||
|
"useUnit": "MB",
|
||||||
|
"useAmount": 36.25
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量 IMEI 查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/sim-imei-batch
|
||||||
|
```
|
||||||
|
|
||||||
|
单次最多查询 1000 张卡。每张卡至少提供 `iccid`、`msisdn`、`imsi` 中的一个;同一条记录提供多个标识时,必须对应同一张卡。
|
||||||
|
|
||||||
|
请求头:
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Access-Token: {accessToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | token 返回的客户组织编码 |
|
||||||
|
| `cards` | array | 卡片标识列表,单次最多 1000 项 |
|
||||||
|
| `cards[].iccid` | string | ICCID;每项与 `msisdn`、`imsi` 至少传一个 |
|
||||||
|
| `cards[].msisdn` | string | MSISDN,可代替 ICCID 标识卡片 |
|
||||||
|
| `cards[].imsi` | string | IMSI,可代替 ICCID 标识卡片 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005473"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msisdn": "1441543605474"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `code` | number | 业务状态码,成功为 `0` |
|
||||||
|
| `message` | string | 错误或提示信息 |
|
||||||
|
| `data.total` | integer | 本次请求的卡片总数 |
|
||||||
|
| `data.matchedCount` | integer | 匹配成功的卡片数量 |
|
||||||
|
| `data.notFoundCount` | integer | 未匹配成功的卡片数量 |
|
||||||
|
| `data.items` | array | 每张卡的查询结果,顺序与请求中的 `cards` 一致 |
|
||||||
|
| `data.items[].matched` | boolean | 是否匹配成功 |
|
||||||
|
| `data.items[].iccid` | string | 匹配成功时返回的 ICCID |
|
||||||
|
| `data.items[].imei` | string | IMEI;尚未采集到该卡 IMEI 时为空字符串 |
|
||||||
|
| `data.items[].errorCode` | string | 匹配失败时返回的错误码;匹配成功时不返回 |
|
||||||
|
| `data.items[].errorMessage` | string | 匹配失败时返回的错误说明;匹配成功时不返回 |
|
||||||
|
| `error` | any | 错误信息字段 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"total": 3,
|
||||||
|
"matchedCount": 2,
|
||||||
|
"notFoundCount": 1,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"imei": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005473",
|
||||||
|
"imei": "865466070817587"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": false,
|
||||||
|
"imei": "",
|
||||||
|
"errorCode": "CARD_NOT_FOUND",
|
||||||
|
"errorMessage": "卡片不存在或无权限"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 物联网卡批量限额信息查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/traffic-cutoff-config
|
||||||
|
```
|
||||||
|
|
||||||
|
根据卡片标识批量查询当前达量断网配置。`cards[]` 中的 `iccid`、`msisdn`、`imsi` 三码至少传一个。
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `cards` | array | 卡片标识列表 |
|
||||||
|
| `cards[].iccid` | string | ICCID |
|
||||||
|
| `cards[].msisdn` | string | MSISDN |
|
||||||
|
| `cards[].imsi` | string | IMSI |
|
||||||
|
| `includeUsage` | boolean | 是否返回套餐和流量使用信息 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005474"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005475"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iccid": "89860400000000005476"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"includeUsage": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.total` | integer | 请求中的卡片数量 |
|
||||||
|
| `data.matchedCount` | integer | 匹配成功的卡片数量 |
|
||||||
|
| `data.notFoundCount` | integer | 未匹配成功的卡片数量 |
|
||||||
|
| `data.items` | array | 每张卡的查询结果 |
|
||||||
|
| `data.items[].matched` | boolean | 是否匹配成功 |
|
||||||
|
| `data.items[].iccid` | string | 平台返回的 ICCID |
|
||||||
|
| `data.items[].trafficCutoffStatus` | integer | 达量断网状态:`0` 未配置、`1` 已开启、`2` 已关闭 |
|
||||||
|
| `data.items[].trafficCutoffStatusName` | string | 达量断网状态名称 |
|
||||||
|
| `data.items[].trafficCutoffThreshold` | number | 达量断网阈值,单位 MB |
|
||||||
|
| `data.items[].trafficCutoffThresholdUnit` | string | 阈值单位,固定为 `MB` |
|
||||||
|
| `data.items[].packageTotalFlowMb` | number | 套餐总流量;`includeUsage=true` 时返回 |
|
||||||
|
| `data.items[].usedFlowMb` | number | 已用流量;`includeUsage=true` 时返回 |
|
||||||
|
| `data.items[].remainingFlowMb` | number | 剩余流量;`includeUsage=true` 时返回 |
|
||||||
|
| `data.items[].errorCode` | string | 匹配失败时返回 |
|
||||||
|
| `data.items[].errorMessage` | string | 匹配失败时返回 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
以下状态、阈值和流量数值用于展示批量响应结构,实际结果以接口查询为准。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"total": 3,
|
||||||
|
"matchedCount": 3,
|
||||||
|
"notFoundCount": 0,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005474",
|
||||||
|
"trafficCutoffStatus": 0,
|
||||||
|
"trafficCutoffStatusName": "未配置",
|
||||||
|
"trafficCutoffThreshold": 0,
|
||||||
|
"trafficCutoffThresholdUnit": "MB",
|
||||||
|
"packageTotalFlowMb": 0,
|
||||||
|
"usedFlowMb": 18,
|
||||||
|
"remainingFlowMb": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005475",
|
||||||
|
"trafficCutoffStatus": 1,
|
||||||
|
"trafficCutoffStatusName": "已开启",
|
||||||
|
"trafficCutoffThreshold": 100,
|
||||||
|
"trafficCutoffThresholdUnit": "MB",
|
||||||
|
"packageTotalFlowMb": 200,
|
||||||
|
"usedFlowMb": 80,
|
||||||
|
"remainingFlowMb": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matched": true,
|
||||||
|
"iccid": "89860400000000005476",
|
||||||
|
"trafficCutoffStatus": 2,
|
||||||
|
"trafficCutoffStatusName": "已关闭",
|
||||||
|
"trafficCutoffThreshold": 50,
|
||||||
|
"trafficCutoffThresholdUnit": "MB",
|
||||||
|
"packageTotalFlowMb": 100,
|
||||||
|
"usedFlowMb": 30,
|
||||||
|
"remainingFlowMb": 70
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 批次状态查询
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/batch-status
|
||||||
|
```
|
||||||
|
|
||||||
|
根据指定批次号查询异步处理状态,支持续费、达量断网等异步批次。
|
||||||
|
|
||||||
|
请求参数:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `appId` | string | 客户组织编码 |
|
||||||
|
| `batchNo` | string | 异步 API 返回的批次号 |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"batchNo": "REN20260910153546001000001"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `data.batchNo` | string | 请求中的批次号 |
|
||||||
|
| `data.batchAsyncStatus` | integer | 异步状态:`0` 已创建、`1` 待处理、`2` 处理中、`3` 成功、`4` 失败、`5` 部分成功 |
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"batchNo": "REN20260910153546001000001",
|
||||||
|
"batchAsyncStatus": 3
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
title: 获取调用凭证
|
||||||
|
description: 使用 appKey、appSecret 换取 V3 接口调用凭证
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 获取调用凭证
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /openapi/v3/query/token
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:使用 `appKey`、`appSecret` 换取接口调用凭证。
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| appKey | string | API 账号标识,在管理后台申请获取 |
|
||||||
|
| appSecret | string | API 密钥,在管理后台申请获取 |
|
||||||
|
| refresh | string | 是否刷新 token:`1` 强制生成新 token,`0` 或留空时尽量复用已有 token |
|
||||||
|
|
||||||
|
请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"appKey": "example-app-key",
|
||||||
|
"appSecret": "example-app-secret"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应结构:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| data.appId | string | 客户组织编码,**后续所有接口请求体中都要传入此值** |
|
||||||
|
| data.accessToken | string | 接口调用凭证,后续放入 `X-Access-Token` |
|
||||||
|
|
||||||
|
::: info 注意:appId ≠ appKey
|
||||||
|
- **`appKey`**(如 `SDYZ`)只用于获取 token,调用 `/openapi/v3/query/token` 后就不再需要
|
||||||
|
- **`appId`**(如 `CUS-202601-0001`)由 token 接口返回,后续调用 sim-info 等接口时,请求体中的 `appId` 字段必须填这个值
|
||||||
|
- 如果传错会导致 `code: 61 "登录已过期"` 错误
|
||||||
|
:::
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"appId": "CUS-202601-0001",
|
||||||
|
"accessToken": "example-access-token"
|
||||||
|
},
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 124 KiB |