zzw
21 小时以前 ffe06be91f95ffd2459ebdb2f9130d1e38d53ea7
emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
@@ -5,7 +5,6 @@
import com.emergencyrepair.business.entity.Acceptance;
import com.emergencyrepair.business.entity.BaseEquipment;
import com.emergencyrepair.business.entity.CompletionInfo;
import com.emergencyrepair.business.entity.Contractor;
import com.emergencyrepair.business.entity.EngineeringMajor;
import com.emergencyrepair.business.entity.Fault;
import com.emergencyrepair.business.entity.ProductNode;
@@ -14,7 +13,6 @@
import com.emergencyrepair.business.mapper.AcceptanceMapper;
import com.emergencyrepair.business.mapper.BaseEquipmentMapper;
import com.emergencyrepair.business.mapper.CompletionInfoMapper;
import com.emergencyrepair.business.mapper.ContractorMapper;
import com.emergencyrepair.business.mapper.EngineeringMajorMapper;
import com.emergencyrepair.business.mapper.FaultMapper;
import com.emergencyrepair.business.mapper.ProductNodeMapper;
@@ -34,10 +32,12 @@
import com.emergencyrepair.system.mapper.SysRoleMenuMapper;
import com.emergencyrepair.system.mapper.SysRoleUserMapper;
import com.emergencyrepair.system.mapper.SysTenantMenuMapper;
import com.emergencyrepair.system.service.ButtonPermissionCatalog;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
@@ -50,6 +50,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component
@Order(2)
@@ -60,7 +61,6 @@
    private final SysRoleMenuMapper roleMenuMapper;
    private final SysTenantMenuMapper tenantMenuMapper;
    private final SysRoleUserMapper roleUserMapper;
    private final ContractorMapper contractorMapper;
    private final EngineeringMajorMapper engineeringMajorMapper;
    private final TeamMajorMapper teamMajorMapper;
    private final CompletionInfoMapper completionInfoMapper;
@@ -71,22 +71,29 @@
    private final AcceptanceMapper acceptanceMapper;
    private final BusinessService businessService;
    private final ObjectMapper objectMapper;
    private final JdbcTemplate jdbcTemplate;
    private final ButtonPermissionCatalog buttonPermissionCatalog;
    private final Map<String, Long> menuIds = new HashMap<>();
    private final Map<String, Long> baseIds = new HashMap<>();
    private final Map<String, Long> productDeviceIds = new HashMap<>();
    private final List<Long> contractorIds = new ArrayList<>();
    private final Map<Long, String> contractorNames = new HashMap<>();
    @Override
    @Transactional
    public void run(String... args) {
        seedMenusAndRoles();
        ensureSystemMenus();
        ensureCompletionMenus();
        normalizeSensitiveTerms();
        normalizeEquipmentTerms();
        seedButtonMenus();
        ensureAdminRoleAssignment();
        ensureMajorConfigMenus();
        ensureRoleMenuAncestors();
        seedMajors();
        seedContractors();
        seedTrees();
        indexProductDevices();
        ensureDemoProductDevices();
        indexProductDevices();
        backfillFaultXNoFromEquipment();
        linkFaultEquipment();
@@ -103,13 +110,14 @@
            createMenu("dashboard", null, "首页门户", "/dashboard", "el-icon-s-home", 1, 10);
            Long fault = createMenu("fault", null, "故障管理", null, "el-icon-warning-outline", 0, 20);
            createMenu("fault-report", fault, "故障上报", "/fault/report", null, 1, 10);
            createMenu("xcb-review", fault, "XCB助理审核", "/fault/review", null, 1, 20);
            createMenu("cmb-approval", fault, "CMB领导审批", "/fault/approval/cmb", null, 1, 30);
            createMenu("jcc-assistant", fault, "JCC助理审批", "/fault/approval/jcc-assistant", null, 1, 40);
            createMenu("jcc-director", fault, "JCC处长审批", "/fault/approval/jcc-director", null, 1, 50);
            createMenu("dispatch", fault, "XCB助理派单", "/fault/dispatch", null, 1, 60);
            createMenu("xcb-review", fault, "rk_XCB助理审核", "/fault/review", null, 1, 20);
            createMenu("cmb-approval", fault, "rk_CMB领导审批", "/fault/approval/cmb", null, 1, 30);
            createMenu("jcc-assistant", fault, "rk_JCC助理审批", "/fault/approval/jcc-assistant", null, 1, 40);
            createMenu("jcc-director", fault, "rk_JCC处长审批", "/fault/approval/jcc-director", null, 1, 50);
            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);
@@ -119,22 +127,31 @@
            createMenu("accept-rating", acceptance, "修后质量评级", "/acceptance/rating", null, 1, 50);
            Long reports = createMenu("reports", null, "统计报表", null, "el-icon-data-analysis", 0, 40);
            createMenu("report-overview", reports, "装备故障总体分析", "/reports/overview", null, 1, 10);
            createMenu("report-overview", reports, "设备故障总体分析", "/reports/overview", null, 1, 10);
            createMenu("report-top", reports, "故障高发设备Top", "/reports/top-equipment", null, 1, 20);
            createMenu("report-team", reports, "按T统计", "/reports/by-team", null, 1, 30);
            createMenu("report-team", reports, "rk_按T统计", "/reports/by-team", null, 1, 30);
            createMenu("report-contractor", reports, "厂家维修情况", "/reports/contractors", null, 1, 40);
            createMenu("report-trend", reports, "故障数/备品备件消耗年度趋势", "/reports/trend", null, 1, 50);
            Long system = createMenu("system", null, "系统管理", null, "el-icon-setting", 0, 50);
            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, "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());
@@ -173,25 +190,293 @@
        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, "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) {
    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();
        }
        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();
        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) {
        SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
                .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1"));
        if (adminRole == null) {
            return;
        }
        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);
        for (String url : urls) {
            SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                    .eq(SysMenu::getUrl, url).last("LIMIT 1"));
            if (menu != null) {
                ensureRoleMenu(adminRole.getId(), menu.getId());
                ensureTenantMenu(1L, menu.getId());
            }
        }
    }
    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.
     */
    private void seedButtonMenus() {
        SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
                .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1"));
        for (Map.Entry<String, List<ButtonPermissionCatalog.ButtonDefinition>> entry : buttonPermissionCatalog.catalog().entrySet()) {
            SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                    .eq(SysMenu::getUrl, entry.getKey()).last("LIMIT 1"));
            if (menu == null || menu.getMenuType() == null || menu.getMenuType() != 1) {
                continue;
            }
            List<ButtonPermissionCatalog.ButtonDefinition> definitions = entry.getValue();
            for (int index = 0; index < definitions.size(); index++) {
                ButtonPermissionCatalog.ButtonDefinition definition = definitions.get(index);
                SysMenu existing = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                        .eq(SysMenu::getPid, menu.getId())
                        .eq(SysMenu::getMenuType, 2)
                        .eq(SysMenu::getPermissions, definition.getCode())
                        .last("LIMIT 1"));
                if (existing != null) {
                    continue;
                }
                SysMenu child = new SysMenu();
                child.setPid(menu.getId());
                child.setName(definition.getName());
                child.setPermissions(definition.getCode());
                child.setMenuType(2);
                child.setSortNo((index + 1) * 10);
                child.setVisible(1);
                child.setCreateTime(LocalDateTime.now());
                child.setUpdateTime(LocalDateTime.now());
                menuMapper.insert(child);
                if (adminRole != null) {
                    ensureRoleMenu(adminRole.getId(), child.getId());
                }
                ensureTenantMenu(1L, child.getId());
            }
        }
    }
    private void ensureRoleMenu(Long roleId, Long menuId) {
        if (roleId == null || menuId == null) {
            return;
        }
        long count = roleMenuMapper.selectCount(Wrappers.<SysRoleMenu>lambdaQuery()
                .eq(SysRoleMenu::getRoleId, roleId).eq(SysRoleMenu::getMenuId, menuId));
        if (count == 0) {
            SysRoleMenu relation = new SysRoleMenu();
            relation.setRoleId(roleId);
            relation.setMenuId(menuId);
            relation.setCreateTime(LocalDateTime.now());
            roleMenuMapper.insert(relation);
        }
    }
    private void ensureTenantMenu(Long tenantId, Long menuId) {
        if (tenantId == null || menuId == null) {
            return;
        }
        long count = tenantMenuMapper.selectCount(Wrappers.<SysTenantMenu>lambdaQuery()
                .eq(SysTenantMenu::getTenantId, tenantId).eq(SysTenantMenu::getMenuId, menuId));
        if (count == 0) {
            SysTenantMenu relation = new SysTenantMenu();
            relation.setTenantId(tenantId);
            relation.setMenuId(menuId);
            relation.setCreateTime(LocalDateTime.now());
            tenantMenuMapper.insert(relation);
        }
    }
    private void ensureAdminRoleAssignment() {
        SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
                .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1"));
        if (adminRole == null) {
            return;
        }
        List<Long> ids = jdbcTemplate.query(
                "SELECT USER_ID FROM CSICZB_ZB.SYS_USER WHERE USER_NAME = 'admin' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)",
                (rs, rowNum) -> rs.getLong(1));
        if (!ids.isEmpty()) {
            Long userId = ids.get(0);
            long count = roleUserMapper.selectCount(Wrappers.<SysRoleUser>lambdaQuery()
                    .eq(SysRoleUser::getRoleId, adminRole.getId())
                    .eq(SysRoleUser::getUserId, userId));
            if (count == 0) {
                SysRoleUser relation = new SysRoleUser();
                relation.setRoleId(adminRole.getId());
                relation.setUserId(userId);
                relation.setCreateTime(LocalDateTime.now());
                roleUserMapper.insert(relation);
            }
        }
        Integer placeholder = jdbcTemplate.queryForObject(
                "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = 1", Integer.class);
        if (placeholder == null || placeholder == 0) {
            roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getUserId, 1L));
        }
    }
    /**
@@ -228,42 +513,26 @@
    }
    private void seedContractors() {
        if (contractorMapper.selectCount(null) > 0) {
            return;
        }
        String[][] rows = {
                {"北方机电修理厂", "北方机电", "厂家", "1", "张工", "0"},
                {"海城动力设备厂", "海城动力", "厂家", "2", "李工", "0"},
                {"联合电气维修中心", "联合电气", "厂家", "3", "王工", "0"},
                {"华远机械修造厂", "华远机械", "厂家", "4", "赵工", "0"},
                {"江海自动化服务部", "江海自动化", "服务商", "5", "周工", "0"},
                {"远航甲板设备公司", "远航甲板", "厂家", "6", "陈工", "0"},
                {"精工液压维修厂", "精工液压", "厂家", "7", "刘工", "0"},
                {"启明控制设备厂", "启明控制", "厂家", "8", "孙工", "0"},
                {"恒信管系工程部", "恒信管系", "服务商", "9", "吴工", "1"},
                {"新城综合维修中心", "新城维修", "综合单位", "10", "郑工", "0"}
        };
        for (String[] row : rows) {
            Contractor contractor = new Contractor();
            contractor.setName(row[0]);
            contractor.setShortName(row[1]);
            contractor.setNature(row[2]);
            contractor.setSortNo(Integer.valueOf(row[3]));
            contractor.setContact(row[4]);
            contractor.setDishonest(Integer.valueOf(row[5]));
            contractor.setCreateTime(LocalDateTime.now());
            contractor.setUpdateTime(LocalDateTime.now());
            contractorMapper.insert(contractor);
            contractorIds.add(contractor.getId());
        contractorIds.clear();
        contractorNames.clear();
        List<Map<String, Object>> rows = jdbcTemplate.query(
                "SELECT DEPT_ID, DEPT_NAME FROM CSICZB_ZB.SYS_DEPT " +
                        "WHERE DEPT_TYPE = 'cj' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL) ORDER BY ORDER_NUM, DEPT_ID",
                (rs, rowNum) -> {
                    Map<String, Object> item = new LinkedHashMap<>();
                    item.put("id", rs.getLong("DEPT_ID"));
                    item.put("name", rs.getString("DEPT_NAME"));
                    return item;
                });
        for (Map<String, Object> item : rows) {
            Long id = ((Number) item.get("id")).longValue();
            contractorIds.add(id);
            contractorNames.put(id, String.valueOf(item.get("name")));
        }
    }
    private void seedTrees() {
        if (productNodeMapper.selectCount(null) == 0) {
            ProductNode model = product("LX-A型综合保障平台", "LX-A", null, 1, "MODEL", null, 1);
            ProductNode x01 = product("X01", "X01", model.getId(), 2, "X_NO", null, 1);
            ProductNode x02 = product("X02", "X02", model.getId(), 2, "X_NO", null, 2);
            ProductNode x03 = product("X03", "X03", model.getId(), 2, "X_NO", null, 3);
        if (baseEquipmentMapper.selectCount(null) == 0) {
            Long power = base("动力系统", "BASE-POWER", null, 1, 1);
            Long electric = base("电气系统", "BASE-ELECTRIC", null, 1, 2);
            Long deck = base("甲板系统", "BASE-DECK", null, 1, 3);
@@ -279,20 +548,6 @@
            Long controlDevice = base("集中控制柜", "DEV-CONTROL", control, 3, 1);
            Long hydraulicDevice = base("液压泵组", "DEV-HYD", hydraulic, 3, 1);
            Long compressorDevice = base("空气压缩机组", "DEV-AIR", auxiliary, 3, 1);
            addProductDevice(x01, "动力系统", "SYS-POWER", "推进子系统", "SUB-PROP", "主推进装置", "DEV-PROP", propulsionDevice, 1);
            addProductDevice(x01, "电气系统", "SYS-ELECTRIC", "电力子系统", "SUB-GRID", "主配电装置", "DEV-DIST", distributionDevice, 2);
            addProductDevice(x01, "甲板系统", "SYS-DECK", "液压子系统", "SUB-HYD", "液压泵组", "DEV-HYD", hydraulicDevice, 3);
            addProductDevice(x02, "动力系统", "SYS-POWER", "传动子系统", "SUB-TRANS", "减速齿轮箱", "DEV-GEAR", gearDevice, 1);
            addProductDevice(x02, "电气系统", "SYS-ELECTRIC", "控制子系统", "SUB-CTRL", "集中控制柜", "DEV-CONTROL", controlDevice, 2);
            addProductDevice(x02, "甲板系统", "SYS-DECK", "辅机子系统", "SUB-AUX", "空气压缩机组", "DEV-AIR", compressorDevice, 3);
            addProductDevice(x03, "动力系统", "SYS-POWER", "推进子系统", "SUB-PROP", "主推进装置", "DEV-PROP", propulsionDevice, 1);
            addProductDevice(x03, "电气系统", "SYS-ELECTRIC", "电力子系统", "SUB-GRID", "主配电装置", "DEV-DIST", distributionDevice, 2);
            addProductDevice(x03, "甲板系统", "SYS-DECK", "液压子系统", "SUB-HYD", "液压泵组", "DEV-HYD", hydraulicDevice, 3);
            addProductDevice(x01, "电气系统", "SYS-ELECTRIC", "控制子系统", "SUB-CTRL", "集中控制柜", "DEV-CONTROL", controlDevice, 4);
            addProductDevice(x02, "动力系统", "SYS-POWER", "推进子系统", "SUB-PROP", "主推进装置改进型", "DEV-PROP-X02", propulsionDevice, 4);
            addProductDevice(x03, "电气系统", "SYS-ELECTRIC", "控制子系统", "SUB-CTRL", "集中控制柜", "DEV-CONTROL", controlDevice, 4);
            addProductDevice(x03, "甲板系统", "SYS-DECK", "辅机子系统", "SUB-AUX", "空气压缩机组", "DEV-AIR", compressorDevice, 4);
        }
    }
@@ -336,8 +591,13 @@
        Fault fault = baseFault(code, xNo, equipment, team, nature, date, description);
        fault.setTaskStatus(status);
        if (!StatusConstants.Task.UNREPORTED.equals(status)) {
            List<Long> ids = Arrays.asList(contractorIds.get(Math.abs(code.hashCode()) % 7), contractorIds.get((Math.abs(code.hashCode()) + 1) % 7));
            String names = contractorMapper.selectBatchIds(ids).stream().map(Contractor::getName).collect(java.util.stream.Collectors.joining("、"));
            int size = contractorIds.size();
            List<Long> ids = Arrays.asList(
                    contractorIds.get(Math.abs(code.hashCode()) % size),
                    contractorIds.get((Math.abs(code.hashCode()) + 1) % size));
            String names = ids.stream().map(contractorNames::get)
                    .filter(java.util.Objects::nonNull)
                    .collect(java.util.stream.Collectors.joining("、"));
            fault.setContractorIds(ids.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining(",")));
            fault.setContractorNames(names);
            fault.setEngineeringNo("LX-" + xNo + "-" + date.toString().replace("-", "").substring(2) + "-001");
@@ -350,9 +610,8 @@
        Fault fault = baseFault(code, xNo, equipment, team, nature, date, description);
        fault.setTaskStatus(StatusConstants.Task.JCC_DIRECTOR_APPROVED);
        Long contractorId = contractorIds.get(contractorIndex % contractorIds.size());
        Contractor contractor = contractorMapper.selectById(contractorId);
        fault.setContractorIds(String.valueOf(contractorId));
        fault.setContractorNames(contractor.getName());
        fault.setContractorNames(contractorNames.get(contractorId));
        fault.setEngineeringNo("LX-" + xNo + "-" + date.toString().replace("-", "").substring(2) + "-001");
        fault.setXcbReviewTime(LocalDateTime.now().minusDays(5));
        faultMapper.insert(fault);
@@ -460,45 +719,7 @@
        }
    }
    private void ensureDemoProductDevices() {
        loadBaseIds();
        ensureProductDevice("X01", "电气系统", "SYS-ELECTRIC", "控制子系统", "SUB-CTRL", "集中控制柜", "DEV-CONTROL", "DEV-CONTROL", 4);
        ensureProductDevice("X02", "动力系统", "SYS-POWER", "推进子系统", "SUB-PROP", "主推进装置改进型", "DEV-PROP-X02", "DEV-PROP", 4);
        ensureProductDevice("X03", "电气系统", "SYS-ELECTRIC", "控制子系统", "SUB-CTRL", "集中控制柜", "DEV-CONTROL", "DEV-CONTROL", 4);
        ensureProductDevice("X03", "甲板系统", "SYS-DECK", "辅机子系统", "SUB-AUX", "空气压缩机组", "DEV-AIR", "DEV-AIR", 4);
    }
    private void ensureProductDevice(String xNo, String system1, String code1, String system2, String code2,
                                     String device, String deviceCode, String baseCode, int sort) {
        if (productDeviceIds.containsKey(deviceKey(xNo, device))) {
            return;
        }
        ProductNode xNode = productNode(2, xNo);
        Long baseId = baseIds.get(baseCode);
        if (xNode == null || baseId == null) {
            return;
        }
        addProductDevice(xNode, system1, code1, system2, code2, device, deviceCode, baseId, sort);
    }
    private void loadBaseIds() {
        if (!baseIds.isEmpty()) {
            return;
        }
        for (BaseEquipment node : baseEquipmentMapper.selectList(null)) {
            if (StringUtils.hasText(node.getCode())) {
                baseIds.putIfAbsent(node.getCode(), node.getId());
            }
        }
    }
    private ProductNode productNode(int level, String name) {
        List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery()
                .eq(ProductNode::getNodeLevel, level)
                .eq(ProductNode::getName, name)
                .last("LIMIT 1"));
        return nodes.isEmpty() ? null : nodes.get(0);
    }
    private void backfillFaultXNoFromEquipment() {
        List<ProductNode> nodes = productNodeMapper.selectList(null);
@@ -607,6 +828,20 @@
        }
    }
    private void normalizeSensitiveTerms() {
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'XCB', 'rk_XCB') WHERE NAME LIKE '%XCB%' AND NAME NOT LIKE '%rk_XCB%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'CMB', 'rk_CMB') WHERE NAME LIKE '%CMB%' AND NAME NOT LIKE '%rk_CMB%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'JCC', 'rk_JCC') WHERE NAME LIKE '%JCC%' AND NAME NOT LIKE '%rk_JCC%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'T队', 'rk_T队') WHERE NAME LIKE '%T队%' AND NAME NOT LIKE '%rk_T队%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'T员', 'rk_T员') WHERE NAME LIKE '%T员%' AND NAME NOT LIKE '%rk_T员%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, '按T统计', 'rk_按T统计') WHERE NAME LIKE '%按T统计%' AND NAME NOT LIKE '%rk_按T统计%'");
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, '涉H', 'rk_涉H') WHERE NAME LIKE '%涉H%' AND NAME NOT LIKE '%rk_涉H%'");
    }
    private void normalizeEquipmentTerms() {
        jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, '\u88c5\u5907', '\u8bbe\u5907') WHERE NAME LIKE '%\u88c5\u5907%'");
    }
    private void normalizeParentSystemSeparators() {
        for (Fault fault : faultMapper.selectList(null)) {
            String normalized = normalizeParentSystem(fault.getParentSystem());
@@ -656,9 +891,9 @@
        fault.setParentSystem(equipment.contains("液压") ? "甲板系统" : equipment.contains("控制") ? "电气系统" : "动力系统");
        fault.setEngineeringMajor(team);
        fault.setTeamMajor(team);
        fault.setReportUnit(team + "T队");
        fault.setReportUnit(team + "rk_T队");
        fault.setReportDate(date);
        fault.setOwner("T员" + xNo);
        fault.setOwner("rk_T员" + xNo);
        fault.setFaultImpact("供气".equals(team) ? "供气" : "运行");
        fault.setFaultNature(nature);
        fault.setEngineeringNameDesc(description);
@@ -747,29 +982,6 @@
        node.setCreateTime(LocalDateTime.now());
        node.setUpdateTime(LocalDateTime.now());
        baseEquipmentMapper.insert(node);
        baseIds.put(code, node.getId());
        return node.getId();
    }
    private ProductNode product(String name, String code, Long parentId, int level, String type, Long baseEquipmentId, int sort) {
        ProductNode node = new ProductNode();
        node.setParentId(parentId == null ? 0L : parentId);
        node.setNodeLevel(level);
        node.setNodeType(type);
        node.setName(name);
        node.setCode(code);
        node.setBaseEquipmentId(baseEquipmentId);
        node.setSortNo(sort);
        node.setCreateTime(LocalDateTime.now());
        node.setUpdateTime(LocalDateTime.now());
        productNodeMapper.insert(node);
        return node;
    }
    private void addProductDevice(ProductNode xNode, String system1, String code1, String system2, String code2,
                                  String device, String code, Long baseId, int sort) {
        ProductNode node1 = product(system1, code1 + "-" + xNode.getCode(), xNode.getId(), 3, "SYSTEM1", null, sort);
        ProductNode node2 = product(system2, code2 + "-" + xNode.getCode(), node1.getId(), 4, "SYSTEM2", null, 1);
        product(device, code + "-" + xNode.getCode(), node2.getId(), 5, "DEVICE", baseId, 1);
    }
}