From 82d07d97089bfe91a369d7bddd4ec09245e45923 Mon Sep 17 00:00:00 2001
From: zzw <228544128@qq.com>
Date: 星期三, 07 十月 2026 12:44:35 +0800
Subject: [PATCH] feat: show user extension attributes in list

---
 emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java |  222 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 190 insertions(+), 32 deletions(-)

diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java b/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
index b186d98..0cc1f2c 100644
--- a/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
@@ -34,6 +34,7 @@
 import com.emergencyrepair.system.mapper.SysTenantMenuMapper;
 import com.emergencyrepair.system.service.ButtonPermissionCatalog;
 import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
@@ -50,6 +51,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @Component
 @Order(2)
@@ -73,6 +75,9 @@
     private final JdbcTemplate jdbcTemplate;
     private final ButtonPermissionCatalog buttonPermissionCatalog;
 
+    @Value("${app.seed-demo-data:false}")
+    private boolean seedDemoData;
+
     private final Map<String, Long> menuIds = new HashMap<>();
     private final Map<String, Long> productDeviceIds = new HashMap<>();
     private final List<Long> contractorIds = new ArrayList<>();
@@ -83,11 +88,13 @@
     public void run(String... args) {
         seedMenusAndRoles();
         ensureSystemMenus();
+        ensureCompletionMenus();
         normalizeSensitiveTerms();
         normalizeEquipmentTerms();
         seedButtonMenus();
         ensureAdminRoleAssignment();
         ensureMajorConfigMenus();
+        ensureRoleMenuAncestors();
         seedMajors();
         seedContractors();
         seedTrees();
@@ -97,7 +104,9 @@
         backfillFaultEquipmentIds();
         upgradeDemoFaultEquipment();
         upgradeDemoMultiEquipmentFault();
-        seedBusinessData();
+        if (seedDemoData) {
+            seedBusinessData();
+        }
         backfillCompletionConsumableCounts();
         normalizeParentSystemSeparators();
     }
@@ -114,6 +123,7 @@
             createMenu("dispatch", fault, "rk_XCB鍔╃悊娲惧崟", "/fault/dispatch", null, 1, 60);
             createMenu("completion-list", fault, "瀹屽伐鎯呭喌澶勭悊", "/fault/completion", null, 1, 70);
             createMenu("completion-report", fault, "瀹屽伐淇℃伅涓婃姤", "/completion/report", null, 1, 80);
+            createMenu("completion-query", fault, "瀹屽伐淇℃伅鏌ラ槄", "/completion/query", null, 1, 90);
 
             Long acceptance = createMenu("acceptance", null, "瀹屽伐楠屾敹", null, "el-icon-circle-check", 0, 30);
             createMenu("accept-application", acceptance, "楠屾敹鐢宠", "/acceptance/application", null, 1, 10);
@@ -130,16 +140,24 @@
             createMenu("report-trend", reports, "鏁呴殰鏁�/澶囧搧澶囦欢娑堣�楀勾搴﹁秼鍔�", "/reports/trend", null, 1, 50);
 
             Long system = createMenu("system", null, "绯荤粺绠$悊", null, "el-icon-setting", 0, 50);
-            createMenu("organization", system, "缁勭粐鏈烘瀯", "/system/organization", null, 1, 5);
-            createMenu("contractors", system, "鎵夸慨鍗曚綅绠$悊", "/system/contractors", null, 1, 10);
-            createMenu("product-tree", system, "浜у搧缁撴瀯鏍戠鐞�", "/system/product-tree", null, 1, 20);
-            createMenu("base-equipment", system, "璁惧鍩虹搴撶鐞�", "/system/base-equipment", null, 1, 30);
-            createMenu("engineering-majors", system, "宸ョ▼涓撲笟閰嶇疆", "/system/engineering-majors", null, 1, 35);
-            createMenu("team-majors", system, "rk_T闃熶笓涓氶厤缃�", "/system/team-majors", null, 1, 36);
-            createMenu("roles", system, "瑙掕壊绠$悊", "/system/roles", null, 1, 40);
-            createMenu("menus", system, "鑿滃崟绠$悊", "/system/menus", null, 1, 50);
-            createMenu("menu-permissions", system, "鑿滃崟鏉冮檺绠$悊", "/system/menu-permissions", null, 1, 60);
-            createMenu("three-member", system, "涓夊憳鎺у埗", "/system/three-member", null, 1, 70);
+            Long systemConfig = createMenu("system-config", system, "绯荤粺閰嶇疆", null, "el-icon-s-tools", 0, 10);
+            createMenu("organization", systemConfig, "缁勭粐鏈烘瀯", "/system/organization", null, 1, 10);
+            createMenu("contractors", systemConfig, "鎵夸慨鍗曚綅绠$悊", "/system/contractors", null, 1, 20);
+            createMenu("product-tree", systemConfig, "浜у搧缁撴瀯鏍�", "/system/product-tree", null, 1, 30);
+            createMenu("base-equipment", systemConfig, "璁惧鍩虹搴撶鐞�", "/system/base-equipment", null, 1, 40);
+            createMenu("engineering-majors", systemConfig, "宸ョ▼涓撲笟閰嶇疆", "/system/engineering-majors", null, 1, 50);
+            createMenu("team-majors", systemConfig, "rk_T闃熶笓涓氶厤缃�", "/system/team-majors", null, 1, 60);
+            createMenu("three-member", systemConfig, "涓夊憳鎺у埗", "/system/three-member", null, 1, 70);
+
+            Long permissionConfig = createMenu("permission-config", system, "鏉冮檺閰嶇疆", null, "el-icon-lock", 0, 20);
+            createMenu("users", permissionConfig, "鐢ㄦ埛绠$悊", "/system/users", null, 1, 10);
+            createMenu("roles", permissionConfig, "瑙掕壊绠$悊", "/system/roles", null, 1, 20);
+            createMenu("menus", permissionConfig, "鑿滃崟绠$悊", "/system/menus", null, 1, 30);
+
+            Long logConfig = createMenu("log-config", system, "鏃ュ織绠$悊", null, "el-icon-document", 0, 30);
+            createMenu("login-logs", logConfig, "鐧诲綍鏃ュ織", "/system/login-logs", null, 1, 10);
+            createMenu("operation-logs", logConfig, "鎿嶄綔鏃ュ織", "/system/operation-logs", null, 1, 20);
+            createMenu("error-logs", logConfig, "閿欒鏃ュ織", "/system/error-logs", null, 1, 30);
         } else {
             for (SysMenu menu : menuMapper.selectList(null)) {
                 menuIds.put(menu.getUrl() == null ? "id-" + menu.getId() : menu.getUrl(), menu.getId());
@@ -178,36 +196,121 @@
         SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                 .eq(SysMenu::getName, "绯荤粺绠$悊")
                 .last("LIMIT 1"));
-        Long pid = system == null ? 0L : system.getId();
-        ensureMenu(pid, "宸ョ▼涓撲笟閰嶇疆", "/system/engineering-majors", 35);
-        ensureMenu(pid, "rk_T闃熶笓涓氶厤缃�", "/system/team-majors", 36);
+        SysMenu config = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getName, "绯荤粺閰嶇疆")
+                .eq(SysMenu::getMenuType, 0)
+                .last("LIMIT 1"));
+        Long pid = config == null ? (system == null ? 0L : system.getId()) : config.getId();
+        ensureMenu(pid, "宸ョ▼涓撲笟閰嶇疆", "/system/engineering-majors", 50);
+        ensureMenu(pid, "rk_T闃熶笓涓氶厤缃�", "/system/team-majors", 60);
     }
 
-    private void ensureMenu(Long pid, String name, String url, int sort) {
-        if (menuMapper.selectCount(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getUrl, url)) > 0) {
-            return;
+    private Long ensureGroupMenu(Long pid, String name, String icon, int sort) {
+        SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getPid, pid)
+                .eq(SysMenu::getName, name)
+                .eq(SysMenu::getMenuType, 0)
+                .last("LIMIT 1"));
+        LocalDateTime now = LocalDateTime.now();
+        if (menu == null) {
+            menu = new SysMenu();
+            menu.setPid(pid);
+            menu.setName(name);
+            menu.setIcon(icon);
+            menu.setMenuType(0);
+            menu.setSortNo(sort);
+            menu.setVisible(1);
+            menu.setCreateTime(now);
+            menu.setUpdateTime(now);
+            menuMapper.insert(menu);
+            return menu.getId();
         }
-        SysMenu menu = new SysMenu();
-        menu.setPid(pid);
-        menu.setName(name);
-        menu.setUrl(url);
-        menu.setMenuType(1);
-        menu.setSortNo(sort);
-        menu.setVisible(1);
-        menu.setCreateTime(LocalDateTime.now());
-        menu.setUpdateTime(LocalDateTime.now());
-        menuMapper.insert(menu);
+        boolean changed = !java.util.Objects.equals(menu.getIcon(), icon)
+                || !java.util.Objects.equals(menu.getSortNo(), sort)
+                || !java.util.Objects.equals(menu.getVisible(), 1);
+        if (changed) {
+            menu.setIcon(icon);
+            menu.setSortNo(sort);
+            menu.setVisible(1);
+            menu.setUpdateTime(now);
+            menuMapper.updateById(menu);
+        }
+        return menu.getId();
+    }
+
+    private Long ensureMenu(Long pid, String name, String url, int sort) {
+        SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getUrl, url).last("LIMIT 1"));
+        LocalDateTime now = LocalDateTime.now();
+        if (menu == null) {
+            menu = new SysMenu();
+            menu.setPid(pid);
+            menu.setName(name);
+            menu.setUrl(url);
+            menu.setMenuType(1);
+            menu.setSortNo(sort);
+            menu.setVisible(1);
+            menu.setCreateTime(now);
+            menu.setUpdateTime(now);
+            menuMapper.insert(menu);
+            return menu.getId();
+        }
+        // Existing menu names are user-managed; startup only repairs structural fields.
+        boolean changed = !java.util.Objects.equals(menu.getPid(), pid)
+                || !java.util.Objects.equals(menu.getSortNo(), sort)
+                || !java.util.Objects.equals(menu.getMenuType(), 1)
+                || !java.util.Objects.equals(menu.getVisible(), 1);
+        if (changed) {
+            menu.setPid(pid);
+            menu.setMenuType(1);
+            menu.setSortNo(sort);
+            menu.setVisible(1);
+            menu.setUpdateTime(now);
+            menuMapper.updateById(menu);
+        }
+        return menu.getId();
+    }
+
+    private void ensureCompletionMenus() {
+        SysMenu fault = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getName, "鏁呴殰绠$悊")
+                .eq(SysMenu::getMenuType, 0)
+                .last("LIMIT 1"));
+        Long pid = fault == null ? 0L : fault.getId();
+        ensureMenu(pid, "瀹屽伐淇℃伅鏌ラ槄", "/completion/query", 90);
+        grantAdminMenus("/completion/query");
     }
 
     private void ensureSystemMenus() {
         SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                 .eq(SysMenu::getName, "\u7cfb\u7edf\u7ba1\u7406").last("LIMIT 1"));
         Long pid = system == null ? 0L : system.getId();
-        ensureMenu(pid, "缁勭粐鏈烘瀯", "/system/organization", 5);
-        ensureMenu(pid, "\u7528\u6237\u7ba1\u7406", "/system/users", 37);
-        ensureMenu(pid, "\u767b\u5f55\u65e5\u5fd7", "/system/login-logs", 65);
-        ensureMenu(pid, "\u64cd\u4f5c\u65e5\u5fd7", "/system/operation-logs", 66);
-        grantAdminMenus("/system/organization", "/system/users", "/system/login-logs", "/system/operation-logs");
+        Long systemConfig = ensureGroupMenu(pid, "绯荤粺閰嶇疆", "el-icon-s-tools", 10);
+        Long permissionConfig = ensureGroupMenu(pid, "鏉冮檺閰嶇疆", "el-icon-lock", 20);
+        Long logConfig = ensureGroupMenu(pid, "鏃ュ織绠$悊", "el-icon-document", 30);
+
+        ensureMenu(systemConfig, "缁勭粐鏈烘瀯", "/system/organization", 10);
+        ensureMenu(systemConfig, "鎵夸慨鍗曚綅绠$悊", "/system/contractors", 20);
+        ensureMenu(systemConfig, "浜у搧缁撴瀯鏍�", "/system/product-tree", 30);
+        ensureMenu(systemConfig, "璁惧鍩虹搴撶鐞�", "/system/base-equipment", 40);
+        ensureMenu(systemConfig, "宸ョ▼涓撲笟閰嶇疆", "/system/engineering-majors", 50);
+        ensureMenu(systemConfig, "rk_T闃熶笓涓氶厤缃�", "/system/team-majors", 60);
+        ensureMenu(systemConfig, "涓夊憳鎺у埗", "/system/three-member", 70);
+
+        ensureMenu(permissionConfig, "鐢ㄦ埛绠$悊", "/system/users", 10);
+        ensureMenu(permissionConfig, "瑙掕壊绠$悊", "/system/roles", 20);
+        ensureMenu(permissionConfig, "鑿滃崟绠$悊", "/system/menus", 30);
+
+        ensureMenu(logConfig, "鐧诲綍鏃ュ織", "/system/login-logs", 10);
+        ensureMenu(logConfig, "鎿嶄綔鏃ュ織", "/system/operation-logs", 20);
+        ensureMenu(logConfig, "閿欒鏃ュ織", "/system/error-logs", 30);
+
+        removeMenuByUrl("/system/menu-permissions");
+        grantAdminMenuIds(systemConfig, permissionConfig, logConfig);
+        grantAdminMenus("/system/organization", "/system/contractors", "/system/product-tree",
+                "/system/base-equipment", "/system/engineering-majors", "/system/team-majors",
+                "/system/three-member", "/system/users", "/system/roles", "/system/menus",
+                "/system/login-logs", "/system/operation-logs", "/system/error-logs");
     }
 
     private void grantAdminMenus(String... urls) {
@@ -226,6 +329,61 @@
         }
     }
 
+    private void grantAdminMenuIds(Long... menuIds) {
+        SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
+                .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1"));
+        if (adminRole == null) {
+            return;
+        }
+        for (Long menuId : menuIds) {
+            ensureRoleMenu(adminRole.getId(), menuId);
+            ensureTenantMenu(1L, menuId);
+        }
+    }
+
+    private void removeMenuByUrl(String url) {
+        SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getUrl, url).last("LIMIT 1"));
+        if (menu == null) {
+            return;
+        }
+        List<SysMenu> children = menuMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getPid, menu.getId()));
+        for (SysMenu child : children) {
+            removeMenuRelations(child.getId());
+            menuMapper.deleteById(child.getId());
+        }
+        removeMenuRelations(menu.getId());
+        menuMapper.deleteById(menu.getId());
+    }
+
+    private void removeMenuRelations(Long menuId) {
+        roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, menuId));
+        tenantMenuMapper.delete(Wrappers.<SysTenantMenu>lambdaQuery().eq(SysTenantMenu::getMenuId, menuId));
+    }
+
+    private void ensureRoleMenuAncestors() {
+        Map<Long, SysMenu> menuById = menuMapper.selectList(null).stream()
+                .collect(java.util.stream.Collectors.toMap(SysMenu::getId, item -> item));
+        List<SysRoleMenu> relations = new ArrayList<>(roleMenuMapper.selectList(null));
+        Set<String> existing = relations.stream()
+                .map(item -> item.getRoleId() + "-" + item.getMenuId())
+                .collect(java.util.stream.Collectors.toSet());
+        for (SysRoleMenu relation : relations) {
+            SysMenu menu = menuById.get(relation.getMenuId());
+            Long cursor = menu == null ? null : menu.getPid();
+            int guard = 0;
+            while (cursor != null && cursor != 0L && guard++ < 32) {
+                String key = relation.getRoleId() + "-" + cursor;
+                if (existing.add(key)) {
+                    ensureRoleMenu(relation.getRoleId(), cursor);
+                }
+                SysMenu parent = menuById.get(cursor);
+                cursor = parent == null ? null : parent.getPid();
+            }
+        }
+    }
+
     /**
      * Materializes the declarative button catalog as MENU_TYPE=2 rows.
      * Existing rows are left untouched so manual menu edits are preserved.

--
Gitblit v1.9.1