diff --git a/migrations/versions/d4e5f6a7b8c9_add_cookie_manager_menus.py b/migrations/versions/d4e5f6a7b8c9_add_cookie_manager_menus.py index 4f371b9..5837d49 100644 --- a/migrations/versions/d4e5f6a7b8c9_add_cookie_manager_menus.py +++ b/migrations/versions/d4e5f6a7b8c9_add_cookie_manager_menus.py @@ -44,37 +44,71 @@ 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: - conn.execute( - sa.text( - "INSERT INTO admin_power " - "(id, name, type, code, url, open_type, parent_id, icon, sort, " - " enable, create_time, update_time) " - "VALUES (:id, :name, :type, :code, :url, :open_type, :parent_id, " - " :icon, :sort, :enable, :ct, :ut)" - ), - { - "id": mid, "name": name, "type": mtype, "code": code, - "url": url, "open_type": open_type, "parent_id": parent_id, - "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}, - ) + 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 " + "(id, name, type, code, url, open_type, parent_id, icon, sort, " + " enable, create_time, update_time) " + "VALUES (:id, :name, :type, :code, :url, :open_type, :parent_id, " + " :icon, :sort, :enable, :ct, :ut)" + ), + { + "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, + }, + ) + id_map[mid] = new_id def downgrade():