fix(migration): Cookie菜单迁移改为按业务键查找并支持 id 顺延

原迁移只按固定 id(76-84) 判断是否存在:若线上 admin_power 的
这些 id 已被其它菜单占用,升级会静默跳过,菜单依旧不出现,
而 start.sh 里 flask db upgrade 的失败/跳过只打 WARN,不易察觉。

调整:
- 先按 code / name+type 业务键查找,命中则仅补 open_type、不重复插入
- 固定 id 被占用时顺延到 max(id)+1,并用 id_map 修正父子 parent_id 关联

已用临时库验证两种场景:
- 76-84 被占用 -> 菜单落在 85-93,父子关联与 open_type 正确
- 重复执行 -> 仍为 9 条,不重复插入
This commit is contained in:
bwstudio
2026-09-10 18:59:31 +08:00
parent ecfb56bb0f
commit 1b68363bfd
@@ -44,16 +44,55 @@ MENUS = [
def upgrade():
"""幂等补齐 Cookie 管理菜单。
"仅按固定 id 判断"不同:先按业务键(code / name+type)查找;
固定 id 若已被其它菜单占用则自动顺延到 max(id)+1,
避免线上 id 分配与本地不一致时静默跳过、菜单仍然不出现。
"""
conn = op.get_bind()
now = datetime.datetime.now()
for mid, name, mtype, code, url, open_type, parent_id, icon, sort, enable in MENUS:
row = conn.execute(
sa.text("SELECT id, open_type FROM admin_power WHERE id = :id"),
{"id": mid},
def next_free_id(preferred):
exist = conn.execute(
sa.text("SELECT id FROM admin_power WHERE id = :i"), {"i": preferred}
).fetchone()
if exist is None:
return preferred
mx = conn.execute(sa.text("SELECT MAX(id) FROM admin_power")).scalar() or 0
return int(mx) + 1
if row is None:
def find_by_code(code):
if not code:
return None
return conn.execute(
sa.text("SELECT id FROM admin_power WHERE code = :c"), {"c": code}
).scalar()
def find_by_name_type(name, mtype):
return conn.execute(
sa.text("SELECT id FROM admin_power WHERE name = :n AND type = :t"),
{"n": name, "t": mtype},
).scalar()
id_map = {} # 迁移里声明的固定 id -> 实际落库的 id(用于修正 parent_id
for mid, name, mtype, code, url, open_type, parent_id, icon, sort, enable in MENUS:
found = find_by_code(code) or find_by_name_type(name, mtype)
if found is not None:
id_map[mid] = int(found)
if open_type:
conn.execute(
sa.text(
"UPDATE admin_power SET open_type = :ot WHERE id = :id "
"AND (open_type IS NULL OR open_type = '')"
),
{"ot": open_type, "id": int(found)},
)
continue
real_parent = id_map.get(parent_id, parent_id) if parent_id else 0
new_id = next_free_id(mid)
conn.execute(
sa.text(
"INSERT INTO admin_power "
@@ -63,18 +102,13 @@ def upgrade():
" :icon, :sort, :enable, :ct, :ut)"
),
{
"id": mid, "name": name, "type": mtype, "code": code,
"url": url, "open_type": open_type, "parent_id": parent_id,
"id": new_id, "name": name, "type": mtype, "code": code,
"url": url, "open_type": open_type, "parent_id": real_parent,
"icon": icon, "sort": sort, "enable": enable,
"ct": now, "ut": now,
},
)
elif not row[1] and open_type:
# 历史数据:菜单已存在但打开方式为空,补齐 _iframe
conn.execute(
sa.text("UPDATE admin_power SET open_type = :ot WHERE id = :id"),
{"ot": open_type, "id": mid},
)
id_map[mid] = new_id
def downgrade():