فهرست منبع

用户模板导入

cr 2 روز پیش
والد
کامیت
cf8f38b52b

+ 1 - 1
blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/ExcelUserTemp.java

@@ -39,7 +39,7 @@ public class ExcelUserTemp {
 
     // I列:项目id → index=8
     @ExcelProperty(index = 8)
-    private String projectName;
+    private String projectId;
 
     // J列:合同段名称/编号 → index=9
     @ExcelProperty(index = 9)

+ 32 - 0
blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/ImportPorjectInfoDTO.java

@@ -0,0 +1,32 @@
+package org.springblade.system.user.dto;
+
+import lombok.Data;
+
+@Data
+public class ImportPorjectInfoDTO{
+    /**
+     * 项目id
+     */
+    private String projectId;
+
+    /**
+     * 合同段名称/编号
+     */
+    private String contractInfo;
+
+    /**
+     * 所属方(1业主方 2监理方 3施工方)
+     */
+    private String owner;
+
+    /**
+     * 岗位名称(中文匹配)
+     */
+    private String roleName;
+
+    private Integer status=0;
+
+    private Long contractId;
+
+    private Long roleId;
+}

+ 2 - 27
blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/UserImporterNew.java

@@ -2,6 +2,7 @@ package org.springblade.system.user.dto;
 
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -46,38 +47,12 @@ public class UserImporterNew {
      * 电签信息(东方中迅电签码)
      */
     private String accCode;
-
-    /**
-     * 项目id
-     */
-    private String projectId;
-
-    /**
-     * 合同段名称/编号
-     */
-    private String contractInfo;
-
-    /**
-     * 所属方(1业主方 2监理方 3施工方)
-     */
-    private String owner;
-
-    /**
-     * 岗位名称(中文匹配)
-     */
-    private String roleName;
-
     /**
      * 登录状态(允许为空,默认为是)
      */
     private String status;
 
-
-
-    private String contractId;
-
-    private Long roleId;
-
     private Long deptId;
 
+    private List<ImportPorjectInfoDTO> projectInfoList=new ArrayList<>();
 }

+ 283 - 361
blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -56,10 +56,7 @@ import org.springblade.system.enums.DictEnum;
 import org.springblade.system.feign.ISysClient;
 import org.springblade.system.user.bean.NodeVO;
 import org.springblade.system.user.cache.UserCache;
-import org.springblade.system.user.dto.ExcelUserTemp;
-import org.springblade.system.user.dto.ImportUserLog;
-import org.springblade.system.user.dto.UserDTO;
-import org.springblade.system.user.dto.UserImporterNew;
+import org.springblade.system.user.dto.*;
 import org.springblade.system.user.entity.*;
 import org.springblade.system.user.enums.UserEnum;
 import org.springblade.system.user.excel.UserExcel;
@@ -2408,47 +2405,61 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                     .build()
                     .excelExecutor()
                     .sheetList();
+            // 遍历每个Sheet,逐个读取并处理数据
             for (ReadSheet sheet : allSheets) {
-                System.out.println("正在读取sheet:" + sheet.getSheetName() + "(索引:" + sheet.getSheetNo() + ")");
+                System.out.printf("开始读取Sheet:%s(索引:%d)%n", sheet.getSheetName(), sheet.getSheetNo());
 
-                // 读取当前sheet数据(从第3行开始,跳过前2行表头)
                 List<ExcelUserTemp> sheetData = EasyExcel.read(file.getInputStream())
                         .head(ExcelUserTemp.class)
                         .sheet(sheet.getSheetNo())
                         .headRowNumber(2)
                         .doReadSync();
 
-                // 3. 核心逻辑:收集同一用户的多个项目名称+合同段,均用逗号拼接
-                ExcelUserTemp currentUser = null; // 记录当前用户核心信息
-                List<String> projectNameList = new ArrayList<>(); // 收集当前用户的所有项目名称
-                List<String> contractList = new ArrayList<>(); // 收集当前用户的所有合同段
+                UserImporterNew currentUser = null;
+                List<ImportPorjectInfoDTO> tempProjectList = new ArrayList<>();
+                String currentProjectId = ""; // 新增:存储当前用户的“基准项目ID”(继承自新用户行)
 
                 for (ExcelUserTemp row : sheetData) {
-                    // 判断当前行是否是“新用户行”(核心字段有值)
-                    if (!isAllCoreFieldsNull(row)) {
-                        // 先处理上一个用户(若存在)
+                    if (!isCoreFieldEmpty(row)) {
+                        // 处理上一个用户
                         if (currentUser != null) {
-                            generateSingleUserRecord(currentUser, projectNameList, contractList, finalUserList);
+                            currentUser.setProjectInfoList(tempProjectList);
+                            finalUserList.add(currentUser);
+                        }
+
+                        // 初始化新用户
+                        currentUser = initUserImporterNew(row);
+                        tempProjectList = new ArrayList<>();
+                        // 新用户行的projectId作为基准ID
+                        currentProjectId = Objects.nonNull(row.getProjectId()) ? row.getProjectId().trim() : "";
+
+                        // 生成新用户行的项目DTO
+                        ImportPorjectInfoDTO projectDTO = buildPorjectInfoDTO(row, currentProjectId);
+                        if (projectDTO != null) {
+                            tempProjectList.add(projectDTO);
                         }
-                        // 初始化新用户信息+项目/合同段列表
-                        currentUser = row;
-                        projectNameList = new ArrayList<>();
-                        contractList = new ArrayList<>();
-                        // 收集当前行的项目名称和合同段
-                        addValidValue(row.getProjectName(), projectNameList);
-                        addValidValue(row.getContractInfo(), contractList);
                     } else {
-                        // 当前行是“补充行”(核心字段空,仅项目名称/合同段有值)
+                        // 补充行:继承当前基准项目ID
                         if (currentUser != null) {
-                            addValidValue(row.getProjectName(), projectNameList);
-                            addValidValue(row.getContractInfo(), contractList);
+                            // 若补充行有新的projectId(如王三5的补充行),更新基准ID
+                            if (Objects.nonNull(row.getProjectId()) && !row.getProjectId().trim().isEmpty()) {
+                                currentProjectId = row.getProjectId().trim();
+                            }
+                            // 生成DTO(传入基准projectId)
+                            ImportPorjectInfoDTO projectDTO = buildPorjectInfoDTO(row, currentProjectId);
+                            if (projectDTO != null) {
+                                if (!isProjectDuplicate(tempProjectList, projectDTO)) {
+                                    tempProjectList.add(projectDTO);
+                                }
+                            }
                         }
                     }
                 }
 
                 // 处理最后一个用户
                 if (currentUser != null) {
-                    generateSingleUserRecord(currentUser, projectNameList, contractList, finalUserList);
+                    currentUser.setProjectInfoList(tempProjectList);
+                    finalUserList.add(currentUser);
                 }
             }
             String file_path = FileUtils.getSysLocalFileUrl();
@@ -2479,13 +2490,13 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                 roleMap.put("1",roleNamesYZ);
                 //查出所有的项目的合同段名
                 // 查出所有的项目的合同段名
-                Set<String> projectIdData = finalUserList.stream()
-                        .filter(userImporterNew -> !StringUtil.isEmpty(userImporterNew.getProjectId()))
-                        .flatMap(userImporterNew -> Arrays.stream(userImporterNew.getProjectId().split(",")))
-                        .map(String::trim)
-                        .filter(projectId -> !projectId.isEmpty())
-                        .collect(Collectors.toSet());
-                List<String>projectIds=contractClient.queryProjectIds(projectIdData);
+                Set<String> projectIdSet = finalUserList.stream()
+                        .filter(user -> user.getProjectInfoList() != null) // 过滤掉projectInfoList为null的用户
+                        .flatMap(user -> user.getProjectInfoList().stream()) // 展平所有项目列表
+                        .filter(project -> project.getProjectId() != null && !project.getProjectId().trim().isEmpty()) // 过滤掉projectId为null或空的项目
+                        .map(project -> project.getProjectId().trim()) // 提取并trim projectId
+                        .collect(Collectors.toSet()); // 收集到Set中去重
+                List<String>projectIds=contractClient.queryProjectIds(projectIdSet);
                 //根据项目id查询合同段
                 Map<String,List<ContractInfo>> contractNamesMap=new HashMap<>();
                 if(!projectIds.isEmpty()){
@@ -2495,6 +2506,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                     }
                 }
                 for (UserImporterNew userImporterNew : finalUserList) {
+                    if(!userImporterNew.getProjectInfoList().isEmpty()){
+                        userImporterNew.getProjectInfoList().removeIf(o->StringUtils.isEmpty(o.getProjectId())||StringUtils.isEmpty(o.getContractInfo()));
+                    }
                     //首先 根据账号查询用户
                     //账号为null
                     if(StringUtil.isEmpty(userImporterNew.getAccount())){
@@ -2538,11 +2552,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                         /**
                          * 验证身份证号
                          */
-                        if(StringUtil.isEmpty(userImporterNew.getIdNumber())){
-                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"身份证号为空",importTime,importId,importUser,filePath);
-                            logs.add(log);
-                            continue;
-                        }else {
+                        if(!StringUtil.isEmpty(userImporterNew.getIdNumber())) {
                             boolean b = isValidIdCard(userImporterNew.getIdNumber());
                             if(!b){
                                 ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"身份证号格式错误",importTime,importId,importUser,filePath);
@@ -2596,102 +2606,101 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                             }
                         }
                         //如果部门信息全都为null 就跳过验证
-                        if(!(StringUtil.isEmpty(userImporterNew.getProjectId())&&StringUtil.isEmpty(userImporterNew.getContractInfo())&&StringUtil.isEmpty(userImporterNew.getOwner())&&StringUtil.isEmpty(userImporterNew.getRoleName()))){
-                            /**
-                             * 验证项目信息
-                             */
-                            if(StringUtil.isEmpty(userImporterNew.getProjectId())){
-                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目为空",importTime,importId,importUser,filePath);
-                                logs.add(log);
-                                continue;
-                            }else {
-                                StringBuilder pid=new StringBuilder();
-                                String projectIdJoin = userImporterNew.getProjectId();
-                                String[] projectIdJoins = projectIdJoin.split(",");
-                                for (String idJoin : projectIdJoins) {
-                                    if(!projectIds.contains(idJoin)){
-                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目Id:"+idJoin+",没找到对应的项目",importTime,importId,importUser,filePath);
+                        if(userImporterNew.getProjectInfoList()!=null&& !userImporterNew.getProjectInfoList().isEmpty()){
+                            List<ImportPorjectInfoDTO> projectInfoList = userImporterNew.getProjectInfoList();
+                            for (ImportPorjectInfoDTO dto : projectInfoList) {
+                                /**
+                                 * 验证项目信息
+                                 */
+                                if(StringUtil.isEmpty(dto.getProjectId())){
+                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目为空",importTime,importId,importUser,filePath);
+                                    logs.add(log);
+                                    dto.setStatus(1);
+                                    continue;
+                                }else {
+                                    if(!projectIds.contains(dto.getProjectId())){
+                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目Id:"+dto.getProjectId()+",没找到对应的项目",importTime,importId,importUser,filePath);
                                         logs.add(log);
-                                    }else {
-                                        pid.append(",").append(idJoin);
-                                    }
-                                }
-                                if(pid!=null&&!StringUtil.isEmpty(pid.toString())){
-                                    userImporterNew.setProjectId(pid.substring(1));
-                                }
-                            }
-                            /**
-                             * 验证合同段
-                             */
-                            if(StringUtil.isEmpty(userImporterNew.getContractInfo())){
-                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号为空",importTime,importId,importUser,filePath);
-                                logs.add(log);
-                                continue;
-                            }else {
-                                StringBuilder contractId=new StringBuilder();
-                                String contractInfo = userImporterNew.getContractInfo();
-                                String[] contractInfoData = contractInfo.split(",");
-                                //根据名称或编号来查找
-                                List<ContractInfo> contractInfos=new ArrayList<>();
-                                String projectId = userImporterNew.getProjectId();
-                                String[] projectIdList = projectId.split(",");
-                                for (String id : projectIdList) {
-                                    List<ContractInfo> infos = contractNamesMap.get(id);
-                                    if(!infos.isEmpty()){
-                                        contractInfos.addAll(infos);
+                                        dto.setStatus(1);
+                                        continue;
                                     }
                                 }
-                                for (String info : contractInfoData) {
-                                    Optional<ContractInfo> nameOptional = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractName()) && c.getContractName().equals(info)).findFirst();
-                                    Optional<ContractInfo> numberOptional  = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractNumber()) && c.getContractNumber().equals(info)).findFirst();
+                                /**
+                                 * 验证合同段
+                                 */
+                                if(StringUtil.isEmpty(dto.getContractInfo())){
+                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号为空",importTime,importId,importUser,filePath);
+                                    logs.add(log);
+                                    dto.setStatus(1);
+                                    continue;
+                                }else {
+                                    List<ContractInfo> contractInfos = contractNamesMap.get(dto.getProjectId());
+                                    Optional<ContractInfo> nameOptional = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractName()) && c.getContractName().equals(dto.getContractInfo())).findFirst();
+                                    Optional<ContractInfo> numberOptional  = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractNumber()) && c.getContractNumber().equals(dto.getContractInfo())).findFirst();
                                     if(!(nameOptional.isPresent()||numberOptional.isPresent())){
-                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号不存在:"+info,importTime,importId,importUser,filePath);
+                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号不存在:"+dto.getContractInfo(),importTime,importId,importUser,filePath);
                                         logs.add(log);
+                                        dto.setStatus(1);
+                                        continue;
                                     }else {
                                         if(nameOptional.isPresent()){
-                                            contractId.append(",").append(nameOptional.get().getId());
+                                            dto.setContractId(nameOptional.get().getId());
                                         }else {
-                                            contractId.append(",").append(numberOptional.get().getId());
+                                            dto.setContractId(numberOptional.get().getId());
                                         }
                                     }
                                 }
-                                if(contractId.toString()!=null&&!StringUtil.isEmpty(contractId.toString())){
-                                    userImporterNew.setContractId(contractId.substring(1));
-                                }
-                            }
-                            /**
-                             * 验证所属方
-                             */
-                            if(StringUtil.isEmpty(userImporterNew.getOwner())){
-                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方为空",importTime,importId,importUser,filePath);
-                                logs.add(log);
-                                continue;
-                            }else {
-                                String owner = userImporterNew.getOwner();
-                                if(!(owner.equals("1")||owner.equals("2")||owner.equals("3"))){
-                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方格式错误",importTime,importId,importUser,filePath);
+                                /**
+                                 * 验证所属方
+                                 */
+                                if(StringUtil.isEmpty(dto.getOwner())){
+                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方为空",importTime,importId,importUser,filePath);
                                     logs.add(log);
+                                    dto.setStatus(1);
                                     continue;
+                                }else {
+                                    String owner = dto.getOwner();
+                                    if(!(owner.equals("1")||owner.equals("2")||owner.equals("3"))){
+                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方格式错误",importTime,importId,importUser,filePath);
+                                        logs.add(log);
+                                        dto.setStatus(1);
+                                        continue;
+                                    }
                                 }
-                            }
-                            /**
-                             * 验证岗位
-                             */
-                            if(StringUtil.isEmpty(userImporterNew.getRoleName())){
-                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色为空",importTime,importId,importUser,filePath);
-                                logs.add(log);
-                            }else {
-                                List<Role> roles = roleMap.get(userImporterNew.getOwner());
-                                Optional<Role> role = roles.stream().filter(r -> r.getRoleName().equals(userImporterNew.getRoleName())).findFirst();
-                                if (role.isPresent()) {
-                                    userImporterNew.setRoleId(role.get().getId());
-                                } else {
-                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色不存在",importTime,importId,importUser,filePath);
+                                /**
+                                 * 验证岗位
+                                 */
+                                if(StringUtil.isEmpty(dto.getRoleName())){
+                                    ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色为空",importTime,importId,importUser,filePath);
                                     logs.add(log);
+                                    dto.setStatus(1);
+                                }else {
+                                    List<Role> roles = roleMap.get(dto.getOwner());
+                                    Optional<Role> role = roles.stream().filter(r -> r.getRoleName().equals(dto.getRoleName())).findFirst();
+                                    if (role.isPresent()) {
+                                        dto.setRoleId(role.get().getId());
+                                    } else {
+                                        ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色不存在",importTime,importId,importUser,filePath);
+                                        logs.add(log);
+                                        dto.setStatus(1);
+                                    }
                                 }
                             }
                         }
                          //验证完毕 开始新增
+                        if(userImporterNew.getProjectInfoList().isEmpty()){
+                            userImporterNew.getProjectInfoList().removeIf(dto -> dto.getStatus()==1);
+                        }
+                        String roleIds = "";
+                        if(userImporterNew.getProjectInfoList().isEmpty()){
+                            if (userImporterNew.getProjectInfoList() != null) {
+                                roleIds = userImporterNew.getProjectInfoList().stream()
+                                        .filter(dto -> dto.getRoleId() != null)
+                                        .map(dto -> String.valueOf(dto.getRoleId())) // 显式转换为字符串
+                                        .distinct()
+                                        .collect(Collectors.joining(","));
+                            }
+                        }
                         /**
                          * 用户账号表
                          */
@@ -2706,7 +2715,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                         insertUser.setRealName(userImporterNew.getRealName());
                         insertUser.setIdNumber(userImporterNew.getIdNumber());
                         insertUser.setPhone(userImporterNew.getPhone());
-                        insertUser.setRoleId(userImporterNew.getRoleId()+"");
+                        insertUser.setRoleId(roleIds);
                         insertUser.setDeptId(userImporterNew.getDeptId()+"");
                         insertUser.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
                         insertUser.setAccCode(userImporterNew.getAccCode());
@@ -2721,26 +2730,17 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                         /**
                          * 项目分配用户关系表
                          */
-                        if(!StringUtil.isEmpty(userImporterNew.getProjectId())&&!StringUtil.isEmpty(userImporterNew.getContractId())){
-                            String projectIdList = userImporterNew.getProjectId();
-                            String[] pids = projectIdList.split(",");
-                            String contractIds = userImporterNew.getContractId();
-                            String[] contractList = contractIds.split(",");
-                            for (String pid : pids) {
-                                List<ContractInfo> contractInfos = contractNamesMap.get(pid);
-                                Set<Long> longContractIds = contractInfos.stream().map(c -> c.getId()).collect(Collectors.toSet());
-                                for (String contractId : contractList) {
-                                    if(longContractIds.contains(Long.valueOf(contractId))){
-                                        SaveUserInfoByProjectDTO dto = new SaveUserInfoByProjectDTO();
-                                        dto.setId(SnowFlakeUtil.getId());
-                                        dto.setProjectId(pid);
-                                        dto.setContractId(contractId);
-                                        dto.setRoleId(insertUser.getRoleId());
-                                        dto.setUserId(insertUser.getId()+"");
-                                        dto.setStatus(1);
-                                        insertUserInfoByProjectList.add(dto);
-                                    }
-                                }
+                        if(userImporterNew.getProjectInfoList().isEmpty()){
+                            List<ImportPorjectInfoDTO> projectInfoList = userImporterNew.getProjectInfoList();
+                            for (ImportPorjectInfoDTO info : projectInfoList) {
+                                SaveUserInfoByProjectDTO dto = new SaveUserInfoByProjectDTO();
+                                dto.setId(SnowFlakeUtil.getId());
+                                dto.setProjectId(info.getProjectId());
+                                dto.setContractId(info.getContractId()+"");
+                                dto.setRoleId(info.getRoleId()+"");
+                                dto.setUserId(insertUser.getId()+"");
+                                dto.setStatus(1);
+                                insertUserInfoByProjectList.add(dto);
                             }
                         }
                     }else {
@@ -2750,206 +2750,104 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                            if(user.getRealName().equals(userImporterNew.getRealName())){
                                //拿到项目和合同段
                                //如果部门信息全都为null 就跳过验证
-                               if(!(StringUtil.isEmpty(userImporterNew.getProjectId())&&StringUtil.isEmpty(userImporterNew.getContractInfo())&&StringUtil.isEmpty(userImporterNew.getOwner())&&StringUtil.isEmpty(userImporterNew.getRoleName()))){
-                                   /**
-                                    * 验证项目信息
-                                    */
-                                   if(StringUtil.isEmpty(userImporterNew.getProjectId())){
-                                       ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目为空",importTime,importId,importUser,filePath);
-                                       logs.add(log);
-                                       continue;
-                                   }else {
-                                       StringBuilder pid=new StringBuilder();
-                                       String projectIdJoin = userImporterNew.getProjectId();
-                                       String[] projectIdJoins = projectIdJoin.split(",");
-                                       for (String idJoin : projectIdJoins) {
-                                           if(!projectIds.contains(idJoin)){
-                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目Id:"+idJoin+",没找到对应的项目",importTime,importId,importUser,filePath);
+                               if(userImporterNew.getProjectInfoList()!=null&& !userImporterNew.getProjectInfoList().isEmpty()){
+                                   List<ImportPorjectInfoDTO> projectInfoList = userImporterNew.getProjectInfoList();
+                                   for (ImportPorjectInfoDTO dto : projectInfoList) {
+                                       /**
+                                        * 验证项目信息
+                                        */
+                                       if(StringUtil.isEmpty(dto.getProjectId())){
+                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目为空",importTime,importId,importUser,filePath);
+                                           logs.add(log);
+                                           dto.setStatus(1);
+                                           continue;
+                                       }else {
+                                           if(!projectIds.contains(dto.getProjectId())){
+                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属项目Id:"+dto.getProjectId()+",没找到对应的项目",importTime,importId,importUser,filePath);
                                                logs.add(log);
-                                           }else {
-                                               pid.append(",").append(idJoin);
-                                           }
-                                       }
-                                       if(pid!=null&&!StringUtil.isEmpty(pid.toString())){
-                                           userImporterNew.setProjectId(pid.substring(1));
-                                       }
-                                   }
-                                   /**
-                                    * 验证合同段
-                                    */
-                                   if(StringUtil.isEmpty(userImporterNew.getContractInfo())){
-                                       ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号为空",importTime,importId,importUser,filePath);
-                                       logs.add(log);
-                                       continue;
-                                   }else {
-                                       StringBuilder contractId=new StringBuilder();
-                                       String contractInfo = userImporterNew.getContractInfo();
-                                       String[] contractInfoData = contractInfo.split(",");
-                                       //根据名称或编号来查找
-                                       List<ContractInfo> contractInfos=new ArrayList<>();
-                                       String projectId = userImporterNew.getProjectId();
-                                       String[] projectIdList = projectId.split(",");
-                                       for (String id : projectIdList) {
-                                           List<ContractInfo> infos = contractNamesMap.get(id);
-                                           if(!infos.isEmpty()){
-                                               contractInfos.addAll(infos);
+                                               dto.setStatus(1);
+                                               continue;
                                            }
                                        }
-                                       for (String info : contractInfoData) {
-                                           Optional<ContractInfo> nameOptional = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractName()) && c.getContractName().equals(info)).findFirst();
-                                           Optional<ContractInfo> numberOptional  = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractNumber()) && c.getContractNumber().equals(info)).findFirst();
+                                       /**
+                                        * 验证合同段
+                                        */
+                                       if(StringUtil.isEmpty(dto.getContractInfo())){
+                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号为空",importTime,importId,importUser,filePath);
+                                           logs.add(log);
+                                           dto.setStatus(1);
+                                           continue;
+                                       }else {
+                                           List<ContractInfo> contractInfos = contractNamesMap.get(dto.getProjectId());
+                                           Optional<ContractInfo> nameOptional = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractName()) && c.getContractName().equals(dto.getContractInfo())).findFirst();
+                                           Optional<ContractInfo> numberOptional  = contractInfos.stream().filter(c -> !StringUtil.isEmpty(c.getContractNumber()) && c.getContractNumber().equals(dto.getContractInfo())).findFirst();
                                            if(!(nameOptional.isPresent()||numberOptional.isPresent())){
-                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号不存在",importTime,importId,importUser,filePath);
+                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"合同段名称/编号不存在:"+dto.getContractInfo(),importTime,importId,importUser,filePath);
                                                logs.add(log);
+                                               dto.setStatus(1);
+                                               continue;
                                            }else {
                                                if(nameOptional.isPresent()){
-                                                   contractId.append(",").append(nameOptional.get().getId());
+                                                   dto.setContractId(nameOptional.get().getId());
                                                }else {
-                                                   contractId.append(",").append(numberOptional.get().getId());
+                                                   dto.setContractId(numberOptional.get().getId());
                                                }
                                            }
                                        }
-                                       if(contractId!=null&&!StringUtil.isEmpty(contractId.toString())){
-                                           userImporterNew.setContractId(contractId.substring(1));
-                                       }
-                                   }
-                                   /**
-                                    * 验证所属方
-                                    */
-                                   if(StringUtil.isEmpty(userImporterNew.getOwner())){
-                                       ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方为空",importTime,importId,importUser,filePath);
-                                       logs.add(log);
-                                       continue;
-                                   }else {
-                                       String owner = userImporterNew.getOwner();
-                                       if(!(owner.equals("1")||owner.equals("2")||owner.equals("3"))){
-                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方格式错误",importTime,importId,importUser,filePath);
+                                       /**
+                                        * 验证所属方
+                                        */
+                                       if(StringUtil.isEmpty(dto.getOwner())){
+                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方为空",importTime,importId,importUser,filePath);
                                            logs.add(log);
+                                           dto.setStatus(1);
                                            continue;
+                                       }else {
+                                           String owner = dto.getOwner();
+                                           if(!(owner.equals("1")||owner.equals("2")||owner.equals("3"))){
+                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属方格式错误",importTime,importId,importUser,filePath);
+                                               logs.add(log);
+                                               dto.setStatus(1);
+                                               continue;
+                                           }
                                        }
-                                   }
-                                   /**
-                                    * 验证岗位
-                                    */
-                                   if(StringUtil.isEmpty(userImporterNew.getRoleName())){
-                                       ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色为空",importTime,importId,importUser,filePath);
-                                       logs.add(log);
-                                   }else {
-                                       List<Role> roles = roleMap.get(userImporterNew.getOwner());
-                                       Optional<Role> role = roles.stream().filter(r -> r.getRoleName().equals(userImporterNew.getRoleName())).findFirst();
-                                       if (role.isPresent()) {
-                                           userImporterNew.setRoleId(role.get().getId());
-                                       } else {
-                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色不存在",importTime,importId,importUser,filePath);
+                                       /**
+                                        * 验证岗位
+                                        */
+                                       if(StringUtil.isEmpty(dto.getRoleName())){
+                                           ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色为空",importTime,importId,importUser,filePath);
                                            logs.add(log);
-                                       }
-                                   }
-                               }
-                               //将当前的项目以及合同段做比较
-                               //查出当前用户分配的项目和合同段
-                               String sql="select * from m_project_assignment_user where user_id="+user.getId()+" and is_deleted=0 and project_id in (" + userImporterNew.getProjectId() + ")";
-                               List<SaveUserInfoByProjectDTO> projectAssignmentUsers = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(SaveUserInfoByProjectDTO.class));
-                               //说明该项目是新增的
-                               if(projectAssignmentUsers.isEmpty()){
-                                   user.setRoleId(user.getRoleId()+","+userImporterNew.getRoleId());
-                                   user.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
-                                   updateUserList.add(user);
-                                   if(!StringUtil.isEmpty(userImporterNew.getProjectId())&&!StringUtil.isEmpty(userImporterNew.getContractId())){
-                                       String projectIdList = userImporterNew.getProjectId();
-                                       String[] pids = projectIdList.split(",");
-                                       String contractIds = userImporterNew.getContractId();
-                                       String[] contractList = contractIds.split(",");
-                                       for (String pid : pids) {
-                                           List<ContractInfo> contractInfos = contractNamesMap.get(pid);
-                                           Set<Long> longContractIds = contractInfos.stream().map(c -> c.getId()).collect(Collectors.toSet());
-                                           for (String cId : contractList) {
-                                               if(longContractIds.contains(Long.valueOf(cId))){
-                                                   SaveUserInfoByProjectDTO dto = new SaveUserInfoByProjectDTO();
-                                                   dto.setId(SnowFlakeUtil.getId());
-                                                   dto.setProjectId(userImporterNew.getProjectId());
-                                                   dto.setContractId(cId);
-                                                   dto.setRoleId(userImporterNew.getRoleId()+"");
-                                                   dto.setUserId(user.getId()+"");
-                                                   dto.setStatus(1);
-                                                   insertUserInfoByProjectList.add(dto);
-                                               }
+                                           dto.setStatus(1);
+                                       }else {
+                                           List<Role> roles = roleMap.get(dto.getOwner());
+                                           Optional<Role> role = roles.stream().filter(r -> r.getRoleName().equals(dto.getRoleName())).findFirst();
+                                           if (role.isPresent()) {
+                                               dto.setRoleId(role.get().getId());
+                                           } else {
+                                               ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"角色不存在",importTime,importId,importUser,filePath);
+                                               logs.add(log);
+                                               dto.setStatus(1);
                                            }
                                        }
                                    }
-                               }else {
-                                   // 现在已经存在的contractIds
-                                   List<String> existContractIds = projectAssignmentUsers.stream().map(p -> p.getContractId()).collect(Collectors.toList());
-
-                                   // 本次模板导入的contractIds
-                                   String contractId = userImporterNew.getContractId();
-                                   List<String> contractIds = Arrays.asList(contractId.split(","));
-
-                                   // 处理逗号分隔的contractId,去除空格并过滤空值
-                                   List<String> processedContractIds = contractIds.stream()
-                                           .map(String::trim)
-                                           .filter(id -> !id.isEmpty())
-                                           .collect(Collectors.toList());
-
-                                  // 转换existContractIds为Set以提高查找效率
-                                   Set<String> existContractIdSet = existContractIds.stream()
-                                           .filter(Objects::nonNull)
-                                           .map(String::trim)
-                                           .filter(id -> !id.isEmpty())
-                                           .collect(Collectors.toSet());
-
-                                   // 1. 本次模板导入的contractIds和existContractIds完全一模一样的情况
-                                   boolean isExactlySame = processedContractIds.size() == existContractIdSet.size()
-                                           && processedContractIds.stream().allMatch(existContractIdSet::contains);
-                                   if(isExactlySame){
-                                       //修改角色
-                                       user.setRoleId(user.getRoleId()+","+userImporterNew.getRoleId());
-                                       user.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
-                                       updateUserList.add(user);
-                                       for (SaveUserInfoByProjectDTO dto : projectAssignmentUsers) {
-                                           dto.setRoleId(user.getRoleId());
-                                           updateUserInfoByProjectList.add(dto);
-                                       }
-                                   }else {
-                                       user.setRoleId(user.getRoleId()+","+userImporterNew.getRoleId());
-                                       user.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
-                                       updateUserList.add(user);
-                                       // 2. 本次导入的contractIds和existContractIds相对比 新增的部分 (存在新增的情况下)
-                                       List<String> newContractIds = processedContractIds.stream()
-                                               .filter(id -> !existContractIdSet.contains(id))
-                                               .collect(Collectors.toList());
-                                       if(!StringUtil.isEmpty(userImporterNew.getProjectId())&&!newContractIds.isEmpty()){
-                                           String projectIdList = userImporterNew.getProjectId();
-                                           String[] pids = projectIdList.split(",");
-                                           for (String pid : pids) {
-                                               List<ContractInfo> contractInfos = contractNamesMap.get(pid);
-                                               Set<Long> longContractIds = contractInfos.stream().map(c -> c.getId()).collect(Collectors.toSet());
-                                               for (String cId : newContractIds) {
-                                                   if(longContractIds.contains(Long.valueOf(cId))){
-                                                       SaveUserInfoByProjectDTO dto = new SaveUserInfoByProjectDTO();
-                                                       dto.setId(SnowFlakeUtil.getId());
-                                                       dto.setProjectId(pid);
-                                                       dto.setContractId(cId);
-                                                       dto.setRoleId(userImporterNew.getRoleId()+"");
-                                                       dto.setUserId(user.getId()+"");
-                                                       dto.setStatus(1);
-                                                       insertUserInfoByProjectList.add(dto);
-                                                   }
-                                               }
-                                           }
-                                       }
-                                       // 3. 本次导入的contractIds和existContractIds相对比 相同的部分 (存在相同的情况下)
-                                       List<String> sameContractIds = processedContractIds.stream()
-                                               .filter(existContractIdSet::contains)
-                                               .collect(Collectors.toList());
-                                       if(!sameContractIds.isEmpty()){
-                                           for (String sameContractId : sameContractIds) {
-                                               for (SaveUserInfoByProjectDTO dto : projectAssignmentUsers) {
-                                                   if(dto.getContractId().equals(sameContractId)){
-                                                       dto.setRoleId(user.getRoleId());
-                                                       updateUserInfoByProjectList.add(dto);
-                                                   }
-                                               }
-                                           }
+                               }
+                               if (userImporterNew.getProjectInfoList() != null && !userImporterNew.getProjectInfoList().isEmpty()) {
+                                   for (ImportPorjectInfoDTO dto : userImporterNew.getProjectInfoList()) {
+                                       String sql = "select * from m_project_assignment_user where user_id=" + user.getId() + " and contract_id=" + dto.getContractId();
+                                       List<SaveUserInfoByProjectDTO> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(SaveUserInfoByProjectDTO.class));
+                                       if (query.isEmpty()) {
+                                           SaveUserInfoByProjectDTO projectdto = new SaveUserInfoByProjectDTO();
+                                           projectdto.setId(SnowFlakeUtil.getId());
+                                           projectdto.setProjectId(dto.getProjectId());
+                                           projectdto.setContractId(dto.getContractId() + "");
+                                           projectdto.setRoleId(dto.getRoleId() + "");
+                                           projectdto.setUserId(user.getId() + "");
+                                           projectdto.setStatus(1);
+                                           insertUserInfoByProjectList.add(projectdto);
+                                       } else {
+                                           SaveUserInfoByProjectDTO projectdto = query.get(0);
+                                           projectdto.setRoleId(dto.getRoleId() + "");
+                                           updateUserInfoByProjectList.add(projectdto);
                                        }
                                    }
                                }
@@ -2993,7 +2891,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
             return R.fail("导入失败:" + e.getMessage());
         }
 
-        return R.success("导入成功,共新增:" + insertUserList.size() + "条有效记录,更新:"+updateUserList.size()+"条记录,失败:"+logs.size()+"条记录");
+        return R.success("导入成功,共新增:" + insertUserList.size() + "条有效记录,更新:"+updateUserInfoByProjectList.size()+"条记录,失败:"+logs.size()+"条记录");
     }
 
     /**
@@ -3028,57 +2926,81 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
     }
 
     /**
-     * 校验并添加有效值(过滤null、空字符串、纯空格)
-     */
-    private void addValidValue(String value, List<String> list) {
-        if (Objects.nonNull(value) && !value.trim().isEmpty()) {
-            list.add(value.trim());
-        }
-    }
-
-    /**
-     * 生成单个用户记录(项目名称+合同段均用逗号拼接)
+     * 初始化UserImporterNew:赋值核心字段+处理默认值
+     * @param row Excel行数据
+     * @return 初始化后的UserImporterNew
      */
-    private void generateSingleUserRecord(ExcelUserTemp currentUser, List<String> projectNameList,
-                                          List<String> contractList, List<UserImporterNew> finalList) {
-        // 处理默认值
-        String defaultPwd = Objects.isNull(currentUser.getPassword()) ? "user123456" : currentUser.getPassword();
-        String defaultStatus = Objects.isNull(currentUser.getStatus()) ? "是" : currentUser.getStatus();
-
-        // 拼接项目名称(空列表存空字符串)
-        String joinedProjectName = projectNameList.isEmpty() ? "" : String.join(",", projectNameList);
-        // 拼接合同段(空列表存空字符串)
-        String joinedContract = contractList.isEmpty() ? "" : String.join(",", contractList);
-
-        // 构建用户记录(仅一条)
+    private UserImporterNew initUserImporterNew(ExcelUserTemp row) {
         UserImporterNew user = new UserImporterNew();
-        user.setAccount(currentUser.getAccount());
+        // 处理默认值:密码默认"user123456",登录状态默认"是"
+        String defaultPwd = Objects.isNull(row.getPassword()) || row.getPassword().trim().isEmpty()
+                ? "user123456"
+                : row.getPassword().trim();
+        String defaultStatus = Objects.isNull(row.getStatus()) || row.getStatus().trim().isEmpty()
+                ? "是"
+                : row.getStatus().trim();
+
+        // 赋值核心用户字段(trim避免空格问题)
+        user.setAccount(Objects.nonNull(row.getAccount()) ? row.getAccount().trim() : "");
         user.setPassword(defaultPwd);
-        user.setRealName(currentUser.getRealName());
-        user.setUserType(currentUser.getUserType());
-        user.setPhone(currentUser.getPhone());
-        user.setIdNumber(currentUser.getIdNumber());
-        user.setDeptName(currentUser.getDeptName());
-        user.setAccCode(currentUser.getAccCode());
-        user.setProjectId(joinedProjectName); // 拼接后的项目名称(后续需转项目id可在此处处理)
-        user.setContractInfo(joinedContract); // 拼接后的合同段
-        user.setOwner(currentUser.getOwner());
-        user.setRoleName(currentUser.getRoleName());
+        user.setRealName(Objects.nonNull(row.getRealName()) ? row.getRealName().trim() : "");
+        user.setUserType(Objects.nonNull(row.getUserType()) ? row.getUserType().trim() : "");
+        user.setPhone(Objects.nonNull(row.getPhone()) ? row.getPhone().trim() : "");
+        user.setIdNumber(Objects.nonNull(row.getIdNumber()) ? row.getIdNumber().trim() : "");
+        user.setDeptName(Objects.nonNull(row.getDeptName()) ? row.getDeptName().trim() : "");
+        user.setAccCode(Objects.nonNull(row.getAccCode()) ? row.getAccCode().trim() : "");
         user.setStatus(defaultStatus);
-
-        finalList.add(user);
-        System.out.println("用户" + currentUser.getRealName() + " - 项目名称:" + joinedProjectName + ",合同段:" + joinedContract);
+        user.setDeptId(null);
+        return user;
     }
+    /**
+     * 构建PorjectInfoDTO:补充行继承基准projectId
+     * @param row Excel行数据
+     * @param baseProjectId 基准项目ID(新用户行的projectId)
+     * @return 有效DTO(projectId非空)
+     */
+    private ImportPorjectInfoDTO buildPorjectInfoDTO(ExcelUserTemp row, String baseProjectId) {
+        // 优先用行内projectId,为空则用基准ID
+        String projectId = Objects.nonNull(row.getProjectId()) ? row.getProjectId().trim() : baseProjectId;
+        // 基准ID也为空则视为无效
+        if (projectId.isEmpty()) {
+            return null;
+        }
 
-
+        ImportPorjectInfoDTO dto = new ImportPorjectInfoDTO();
+        dto.setProjectId(projectId);
+        dto.setContractInfo(Objects.nonNull(row.getContractInfo()) ? row.getContractInfo().trim() : "");
+        dto.setOwner(Objects.nonNull(row.getOwner()) ? row.getOwner().trim() : "");
+        dto.setRoleName(Objects.nonNull(row.getRoleName()) ? row.getRoleName().trim() : "");
+        return dto;
+    }
+    /**
+     * 判断项目是否重复:按「项目ID+合同段」组合去重
+     * @param projectList 已存在的项目列表
+     * @param newDTO 新项目DTO
+     * @return true=重复,false=不重复
+     */
+    private boolean isProjectDuplicate(List<ImportPorjectInfoDTO> projectList, ImportPorjectInfoDTO newDTO) {
+        for (ImportPorjectInfoDTO existDTO : projectList) {
+            // 项目ID和合同段均相同 → 视为重复
+            boolean isSameProjectId = existDTO.getProjectId().equals(newDTO.getProjectId());
+            boolean isSameContract = existDTO.getContractInfo().equals(newDTO.getContractInfo());
+            if (isSameProjectId && isSameContract) {
+                return true;
+            }
+        }
+        return false;
+    }
     /**
-     * 判断核心字段是否全为空(识别新用户行)
+     * 判断核心字段是否为空:用于识别「新用户行」vs「补充行」
+     * 核心字段:登录账号+用户姓名+手机号码(至少一个非空即为新用户行)
+     * @param row Excel行数据
+     * @return true=核心字段全空(补充行),false=核心字段有值(新用户行)
      */
-    private boolean isAllCoreFieldsNull(ExcelUserTemp temp) {
-        return (Objects.isNull(temp.getAccount()) || temp.getAccount().trim().isEmpty())
-                && (Objects.isNull(temp.getRealName()) || temp.getRealName().trim().isEmpty())
-                && (Objects.isNull(temp.getPhone()) || temp.getPhone().trim().isEmpty())
-                && (Objects.isNull(temp.getIdNumber()) || temp.getIdNumber().trim().isEmpty());
+    private boolean isCoreFieldEmpty(ExcelUserTemp row) {
+        return (Objects.isNull(row.getAccount()) || row.getAccount().trim().isEmpty())
+                && (Objects.isNull(row.getRealName()) || row.getRealName().trim().isEmpty())
+                && (Objects.isNull(row.getPhone()) || row.getPhone().trim().isEmpty());
     }
     /**
      * 判断中国大陆手机号格式是否正确