跳转至

外系统菜单批量导入技能

📄 创建: 吴乐 2026-08-12

https://doc.xpku.com/raw/tools/skills/menu-import.md

把另一个系统的菜单树(JSON)批量写入本系统 sys_resource 表,以 iframe 外链方式挂载。 适用场景:综合管控平台接入第三方系统。

定位

第三方系统有自己的前端页面和菜单,不想重写页面,用 iframe 直接嵌入。步骤:拿对方菜单 JSON → 按规则生成 SQL → 导入 sys_resource

输入

  1. 菜单 JSON(对方接口返回,格式见附录):含 name/code/icon/route/is_hidden/sort/parent_id/children 树形结构
  2. 基础 URL:对方系统地址(如 http://ip:port/#
  3. 鉴权参数?key=xxx?access_token=xxx(免登录参数,从对方系统获取)
  4. APP_ID:本系统的应用标识(如 XNDCTPF 等)
  5. 目标数据库bams-apex 库,MySQL 容器名 mysql,账号密码见部署文档

字段映射规则(关键)

sys_resource 字段 来源 规则
id 生成 19位雪花ID(本地自增计数器即可)
pid 生成 父菜单的新雪花ID;根菜单为 0
menu_type 固定 dir
menu_code JSON code 原样
menu_name JSON name 原样
menu_icon JSON icon 原样(MessageBox/List/Menu等)
path JSON route 去掉所有 /(如 /carbonInventorycarbonInventory
sort JSON sort 原样
is_hide JSON is_hidden is_hidden==1 → 1(隐藏),否则 0
IFRAME_SRC 拼接 基础URL + route + 鉴权参数(如 http://ip:port/#/home/index?access_token=xxx
APP_ID 指定 应用标识
IS_ENABLE 固定 1
OPEN_TYPE 固定 '0'
is_cache/is_external_link/IS_DEFAULT_EXPAND 固定 0
created_by/updated_by 固定 103
corp_code 固定 120101

导入步骤

① 写 Python 生成脚本(本地 /tmp/gen_menu.py)

import json

TOKEN = "<鉴权token,向对方系统获取>"
BASE = "<对方系统地址>"      # 如 http://ip:port/#
APP = "<APP_ID>"            # 如 TPF
NOW = "2026-08-12 12:00:00"
CORP = "120101"
UID = "103"

with open("/tmp/menus.json") as f:
    data = json.load(f)

c = [1786100000000000000]   # 计数器,起点每次换一批避免撞ID

def nid():
    c[0] += 1
    return str(c[0])

def process(items, pid="0"):
    sqls = []
    for item in items:
        sid = nid()
        route = item["route"].replace("\\/", "/").strip("/")
        name = item["name"]; icon = item.get("icon", ""); code = item["code"]
        is_hide = 1 if item.get("is_hidden") == 1 else 0
        sort = item.get("sort", 0)
        iframe = f"{BASE}/{route}?access_token={TOKEN}"
        path = route.replace("/", "")
        sql = (f"INSERT INTO sys_resource (id,pid,menu_type,menu_code,menu_name,"
               f"menu_icon,path,sort,is_cache,is_hide,is_external_link,remark,"
               f"IS_ENABLE,MENU_GROUP,IS_DEFAULT_EXPAND,BACKGROUND,CREATE_DEPT,"
               f"CREATE_ORG,OPEN_TYPE,APP_ID,PARAMS,IFRAME_SRC,APP_PRJ_CODE,"
               f"created_by,created_time,updated_by,updated_time,corp_code) VALUES ("
               f"'{sid}','{pid}','dir','{code}','{name}','{icon}','{path}',{sort},"
               f"0,{is_hide},0,NULL,1,'',0,'',NULL,NULL,'0','{APP}','','{iframe}','',"
               f"{UID},'{NOW}',{UID},'{NOW}','{CORP}');")
        sqls.append(sql)
        if "children" in item:
            sqls.extend(process(item["children"], sid))
    return sqls

sqls = process(data["data"])
print(f"DELETE FROM sys_resource WHERE APP_ID='{APP}';")
for s in sqls: print(s)

② 生成 SQL 并导入

python3 /tmp/gen_menu.py > /tmp/menu_sql.txt
scp /tmp/menu_sql.txt root@<服务器>:/home/apex/
ssh root@<服务器> "docker exec -i mysql mysql -uroot -p'<密码>' --default-character-set=utf8mb4 bams-apex < /home/apex/menu_sql.txt"

③ 导入后校验(两处易错点)

-- 1. path 不能带 '/'
SELECT id, path FROM sys_resource WHERE APP_ID='<APP>' AND path LIKE '%/%';
-- 有结果则:UPDATE sys_resource SET path = REPLACE(path,'/','') WHERE APP_ID='<APP>';

-- 2. IFRAME_SRC 不能出现 '#//'
SELECT id, IFRAME_SRC FROM sys_resource WHERE APP_ID='<APP>' AND IFRAME_SRC LIKE '%#//%';
-- 有结果则:UPDATE sys_resource SET IFRAME_SRC = REPLACE(IFRAME_SRC,'#//','#/') WHERE APP_ID='<APP>';

踩过的坑

  • path 带 /:旧系统 path 存的是路由,本系统要求无 /,必须清洗
  • IFRAME_SRC 双斜杠BASE 结尾有 # 时拼接 route 易产生 #//,要 #/
  • DELETE 误删旧数据:导入前 DELETE WHERE APP_ID='xxx' 会连手工配的老记录一起删,注意保留原始记录
  • ID 冲突:雪花ID计数器起点每次换一批,避免撞已有数据
  • shell 脚本 UID 变量:bash 里 UID 是只读系统变量,赋值会报错;用 Python 无此问题

附录:对方菜单 JSON 格式样例

{
  "code": 200,
  "data": [
    {
      "id": 6113,
      "parent_id": 0,
      "name": "示例菜单",
      "code": "home",
      "icon": "HomeFilled",
      "route": "/home/index",
      "is_hidden": 0,
      "sort": 70,
      "children": []
    }
  ]
}
  • 根菜单:parent_id=0
  • 子菜单:parent_id = 父菜单的 id,出现在父菜单的 children 数组里
  • is_hidden: 1 表示隐藏页(详情页等),导入时 is_hide=1

相关笔记

  • 相关技能:tools/skills/agent-watch.md