Browse Source

用户模板导入

用户导入
cr 1 week ago
parent
commit
2cbe390f76
20 changed files with 1277 additions and 10 deletions
  1. 10 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ContractClient.java
  2. 7 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/SaveUserInfoByProjectClient.java
  3. 59 0
      blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/ExcelUserTemp.java
  4. 32 0
      blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/ImportPorjectInfoDTO.java
  5. 19 0
      blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/ImportUserLog.java
  6. 58 0
      blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/dto/UserImporterNew.java
  7. 138 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  8. 20 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ContractClientImpl.java
  9. 29 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/SaveUserInfoByProjectClientImpl.java
  10. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractInfoMapper.java
  11. 10 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractInfoMapper.xml
  12. 3 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ProjectInfoMapper.java
  13. 9 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ProjectInfoMapper.xml
  14. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractInfoService.java
  15. 5 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IProjectInfoService.java
  16. 5 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java
  17. 14 4
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ProjectInfoServiceImpl.java
  18. 37 5
      blade-service/blade-user/src/main/java/org/springblade/system/user/controller/UserController.java
  19. 3 0
      blade-service/blade-user/src/main/java/org/springblade/system/user/service/IUserService.java
  20. 815 1
      blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

+ 10 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ContractClient.java

@@ -4,6 +4,7 @@ package org.springblade.manager.feign;
 import org.springblade.core.tool.api.R;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.entity.ContractInfo;
+import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.vo.RoleSignPfxUserVO3;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.transaction.annotation.Transactional;
@@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 
 import java.util.List;
+import java.util.Set;
 
 import static org.springblade.core.launch.constant.AppConstant.APPLICATION_NAME_PREFIX;
 
@@ -80,4 +82,12 @@ public interface ContractClient {
     @GetMapping(API_PREFIX+"/findAllUserAndRoleList")
     RoleSignPfxUserVO3 findAllUserAndRoleList(@RequestParam Long contractId, @RequestParam(required = false) Long roleId);
 
+    @PostMapping(API_PREFIX+"/queryProjectList")
+    List<ProjectInfo> queryProjectList(@RequestBody Set<String> projectId);
+
+    @PostMapping(API_PREFIX+"/queryProjectIds")
+    List<String> queryProjectIds(@RequestBody Set<String> projectId);
+
+    @GetMapping(API_PREFIX+"/queryContractNamesListByProjectId")
+    List<ContractInfo> queryContractNamesListByProjectId(@RequestParam String projectId);
 }

+ 7 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/SaveUserInfoByProjectClient.java

@@ -23,6 +23,8 @@ public interface SaveUserInfoByProjectClient {
     String SEARCH_USER_INFO_AND_PROJECT_BY_USER_IDS = "/api/manager/searchUserInfoAndProjectByUserIds";
     String QUERY_USER_CONTRACT_ROLE = "/api/manager/queryUserContractRole";
     String SAVE_INFO_RELATION = "/api/manager/saveInfoRelation";
+    String INSERT_USER_INFO_BATCH = "/api/manager/insertUserInfoBatch";
+    String UPDATE_USER_INFO_BATCH = "/api/manager/updateUserInfoBatch";
 
     @PostMapping(QUERY_USER_CONTRACT_ROLE)
     List<JSONObject> queryUserContractRole(@RequestBody List<Long> userIds, @RequestParam String contractId);
@@ -51,4 +53,9 @@ public interface SaveUserInfoByProjectClient {
     @PostMapping(SAVE_INFO_RELATION)
     void saveInfoRelation(@RequestParam Long userId, @RequestParam Long projectId, @RequestParam Long contractId, @RequestParam Long roleId);
 
+    @PostMapping(INSERT_USER_INFO_BATCH)
+    void insertUserInfoBatch(@RequestBody List<SaveUserInfoByProjectDTO>list);
+
+    @PostMapping(UPDATE_USER_INFO_BATCH)
+    void updateUserInfoBatch(@RequestBody List<SaveUserInfoByProjectDTO> updateUserInfoByProjectList);
 }

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

@@ -0,0 +1,59 @@
+package org.springblade.system.user.dto;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+@Data
+public class ExcelUserTemp {
+    // A列:登录账号 → index=0
+    @ExcelProperty(index = 0)
+    private String account;
+
+    // B列:登录密码 → index=1
+    @ExcelProperty(index = 1)
+    private String password;
+
+    // C列:用户姓名 → index=2
+    @ExcelProperty(index = 2)
+    private String realName;
+
+    // D列:用户平台 → index=3
+    @ExcelProperty(index = 3)
+    private String userType;
+
+    // E列:手机号码 → index=4
+    @ExcelProperty(index = 4)
+    private String phone;
+
+    // F列:身份证号码 → index=5
+    @ExcelProperty(index = 5)
+    private String idNumber;
+
+    // G列:所属部门 → index=6
+    @ExcelProperty(index = 6)
+    private String deptName;
+
+    // H列:电签信息 → index=7
+    @ExcelProperty(index = 7)
+    private String accCode;
+
+    // I列:项目id → index=8
+    @ExcelProperty(index = 8)
+    private String projectId;
+
+    // J列:合同段名称/编号 → index=9
+    @ExcelProperty(index = 9)
+    private String contractInfo;
+
+    // K列:所属方 → index=10
+    @ExcelProperty(index = 10)
+    private String owner;
+
+    // L列:岗位名称 → index=11
+    @ExcelProperty(index = 11)
+    private String roleName;
+
+    // M列:登录状态 → index=12
+    @ExcelProperty(index = 12)
+    private String status;
+}

+ 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;
+}

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

@@ -0,0 +1,19 @@
+package org.springblade.system.user.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImportUserLog {
+    private Long id;
+    private String account;
+    private String name;
+    private String message;
+    private String importTime;
+    private Long importId;
+    private Long importUser;
+    private String filePath;
+}

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

@@ -0,0 +1,58 @@
+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;
+
+@Data
+public class UserImporterNew {
+    /**
+     * 登录账号
+     */
+    private String account;
+    /**
+     * 登录密码
+     */
+    private String password;
+
+    /**
+     * 用户姓名
+     */
+    private String realName;
+
+    /**
+     * 用户平台
+     */
+    private String userType;
+
+    /**
+     * 手机号码
+     */
+    private String phone;
+
+    /**
+     * 身份证号码
+     */
+    private String idNumber;
+
+    /**
+     * 所属部门(中文名称)
+     */
+    private String deptName;
+
+    /**
+     * 电签信息(东方中迅电签码)
+     */
+    private String accCode;
+    /**
+     * 登录状态(允许为空,默认为是)
+     */
+    private String status;
+
+    private Long deptId;
+
+    private List<ImportPorjectInfoDTO> projectInfoList=new ArrayList<>();
+}

+ 138 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -39,6 +39,15 @@ import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.MemoryUsageSetting;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.graphics.PDXObject;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.bytedeco.javacv.Java2DFrameConverter;
+import org.bytedeco.javacv.OpenCVFrameConverter;
 import org.springblade.archive.dto.*;
 import org.springblade.archive.entity.*;
 import org.springblade.archive.mapper.ArchiveConclusionMapper;
@@ -1717,6 +1726,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		archiveAutoPdfService.buildArchiveFrontPdfs(archivesAuto.getProjectId(), archivesAuto, waitArchiveFiles, true);
 		//生成页码
 		builtFilePageNo(archivesAuto, waitArchiveFiles);
+        archivesAuto.setColourStatus(0);
+        baseMapper.updateById(archivesAuto);
 		archiveFileClient.updateArchiveFile(waitArchiveFiles);
 	}
 
@@ -5624,6 +5635,133 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 		return sb.toString();
 	}
+
+	@Override
+	public Boolean updateArchivePage(List<Long> archiveIds) {
+		for (Long archiveId : archiveIds) {
+			ArchivesAuto archivesAuto = this.getById(archiveId);
+			List<ArchiveFile> files = archiveFileClient.getArchiveFileByArchivesId(archiveId+"", "");
+			if(!files.isEmpty()){
+				archivesAuto.setFileN(files.size());
+				archivesAuto.setPageN(files.stream().mapToInt(ArchiveFile::getFilePage).sum());
+				archivesAuto.setColourStatus(2);
+				baseMapper.updateById(archivesAuto);
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public String saveVolume(SaveVolumeDto dto) {
+		List<Long>updateIds=new ArrayList<>();
+		ArchivesAuto archivesAuto = this.getById(dto.getArchiveId());
+		archivesAuto.setName(dto.getArchiveName());
+		updateIds.add(archivesAuto.getId());
+		if(!dto.getList().isEmpty()){
+			archivesAuto.setIsVolume(1);
+			archivesAuto.setVolumeIds("");
+			for (SaveVolumeDto1 saveVolumeDto1 : dto.getList()) {
+				Long archiveId;
+				if(saveVolumeDto1.getId()==null){
+					ArchivesAuto auto = new ArchivesAuto();
+					BeanUtil.copy(archivesAuto,auto);
+					auto.setId(SnowFlakeUtil.getId());
+					auto.setName(saveVolumeDto1.getArchiveName());
+					auto.setIsVolume(2);
+
+					archiveId= auto.getId();
+					baseMapper.insert(auto);
+				}else {
+					archiveId=saveVolumeDto1.getId();
+					ArchivesAuto auto = this.getById(archiveId);
+					auto.setName(saveVolumeDto1.getArchiveName());
+					baseMapper.updateById(auto);
+					String update="update u_archive_file set archive_id=old_archive_id,old_archive_id=null,is_volume = 0 where archive_id="+archiveId;
+					jdbcTemplate.update(update);
+				}
+				updateIds.add(archiveId);
+				String update = "UPDATE u_archive_file SET archive_id = ?, old_archive_id = ?, is_volume = 1 WHERE id IN (" + saveVolumeDto1.getFileIds() + ")";
+				jdbcTemplate.update(update, archiveId, dto.getArchiveId());
+				String volumeIds = archivesAuto.getVolumeIds();
+				if(StringUtils.isEmpty(volumeIds)){
+					volumeIds="";
+				}
+				archivesAuto.setVolumeIds(volumeIds+archiveId+",");
+				List<ArchiveFile> files1 = archiveFileClient.getArchiveFileByArchivesId(archiveId + "", "");
+				if(!files1.isEmpty()){
+					List<Integer> sorts = files1.stream()
+							.map(file -> Optional.ofNullable(file.getSort()).orElse(0))
+							.sorted()
+							.collect(Collectors.toList());
+					List<Integer> sorts1 = files1.stream()
+							.map(file -> Optional.ofNullable(file.getArchiveSort()).orElse(0))
+							.sorted()
+							.collect(Collectors.toList());
+					String[] fileIds = saveVolumeDto1.getFileIds().split(",");
+					if(sorts.size()==fileIds.length){
+						if(fileIds.length>0){
+							for (int i = 0; i < fileIds.length; i++) {
+								String updateSql = "UPDATE u_archive_file SET sort = ? ,archive_sort=? WHERE id = ?";
+								jdbcTemplate.update(updateSql, sorts.get(i), sorts1.get(i),fileIds[i]);
+							}
+						}
+					}
+				}
+			}
+			archivesAuto.setVolumeIds(archivesAuto.getVolumeIds().substring(0,archivesAuto.getVolumeIds().length()-1));
+		}
+		baseMapper.updateById(archivesAuto);
+		this.updateArchivePage(updateIds);
+		return "操作成功";
+	}
+
+	@Override
+	public boolean removeVolume(Long archiveId, Long volumeId) {
+		ArchivesAuto auto = this.getById(archiveId);
+		if(auto.getIsVolume()==1&&StringUtils.isNotEmpty(auto.getVolumeIds())){
+			String volumeIds = auto.getVolumeIds();
+			String[] archivesAutoIds = volumeIds.split(",");
+			if(archivesAutoIds.length<=1){
+				throw new ServiceException("不能删除所有的分卷");
+			}
+			if(auto.getVolumeIds().contains(volumeId+"")){
+				if(auto.getVolumeIds().endsWith(volumeId+"")){
+					auto.setVolumeIds(auto.getVolumeIds().replace(volumeId+"",""));
+				}else {
+					auto.setVolumeIds(auto.getVolumeIds().replace(volumeId+",",""));
+				}
+				if(auto.getVolumeIds().startsWith(",")){
+					auto.setVolumeIds(auto.getVolumeIds().substring(1));
+				}
+				if(auto.getVolumeIds().endsWith(",")){
+					auto.setVolumeIds(auto.getVolumeIds().substring(0,auto.getVolumeIds().length()-1));
+				}
+				String update = "UPDATE u_archive_file SET archive_id = ?, old_archive_id = null, is_volume = 0 WHERE archive_id = ?";
+				jdbcTemplate.update(update, archiveId, volumeId);
+			}
+			baseMapper.updateById(auto);
+			this.deleteLogic(Arrays.asList(volumeId));
+			this.updateArchivePage(Arrays.asList(archiveId,volumeId));
+		}
+		return true;
+	}
+
+	@Override
+	public boolean backVolume(Long fileId){
+		ArchiveFile archiveFile = archiveFileClient.getArchiveFileById(fileId);
+		Long id=archiveFile.getArchiveId();
+		List<ArchiveFile> files = archiveFileClient.getArchiveFileByArchivesId(archiveFile.getArchiveId()+"","");
+		if(files.size()<=1){
+			throw new ServiceException("回退失败");
+		}
+		if(archiveFile.getOldArchiveId()!=null){
+			archiveFile.setArchiveId(archiveFile.getOldArchiveId());
+			archiveFile.setIsVolume(0);
+			archiveFileClient.updateById2(archiveFile);
+			this.updateArchivePage(Arrays.asList(id,archiveFile.getArchiveId()));
+		}
+		return true;
+	}
 }
 
 

+ 20 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ContractClientImpl.java

@@ -8,14 +8,18 @@ import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.entity.ContractInfo;
+import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.mapper.SaveUserInfoByProjectMapper;
 import org.springblade.manager.service.IContractInfoService;
+import org.springblade.manager.service.IProjectInfoService;
 import org.springblade.manager.service.impl.SaveUserInfoByProjectServiceImpl;
 import org.springblade.manager.vo.RoleSignPfxUserVO3;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 @RestController
 @AllArgsConstructor
@@ -24,6 +28,7 @@ public class ContractClientImpl implements ContractClient {
     private final IContractInfoService contractInfoService;
     private final SaveUserInfoByProjectMapper saveUserInfoByProjectMapper;
     private final SaveUserInfoByProjectServiceImpl saveUserInfoByProject;
+    private final IProjectInfoService projectInfoService;
 
     @Override
     public List<String> getProcessContractByJLContractId(String contractId) {
@@ -107,5 +112,20 @@ public class ContractClientImpl implements ContractClient {
         return contractInfoService.findAllUserAndRoleList(contractId,roleId);
     }
 
+    @Override
+    public List<ProjectInfo> queryProjectList(Set<String> projectIds) {
+        return projectInfoService.selectProjectList(projectIds);
+    }
+
+    @Override
+    public List<String> queryProjectIds(Set<String> projectId) {
+        return projectInfoService.queryProjectIds(projectId);
+    }
+
+    @Override
+    public List<ContractInfo> queryContractNamesListByProjectId(String projectId) {
+        return contractInfoService.queryContractNamesListByProjectId(projectId);
+    }
+
 
 }

+ 29 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/feign/SaveUserInfoByProjectClientImpl.java

@@ -9,6 +9,8 @@ import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.entity.SaveUserInfoByProject;
 import org.springblade.manager.service.SaveUserInfoByProjectService;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.SingleColumnRowMapper;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
@@ -19,6 +21,7 @@ import java.util.List;
 public class SaveUserInfoByProjectClientImpl implements SaveUserInfoByProjectClient {
 
     private final SaveUserInfoByProjectService saveUserInfoByProjectService;
+    private final JdbcTemplate jdbcTemplate;
 
     @Override
     public List<JSONObject> queryUserContractRole(List<Long> userIds, String contractId) {
@@ -65,5 +68,31 @@ public class SaveUserInfoByProjectClientImpl implements SaveUserInfoByProjectCli
         saveUserInfoByProjectService.save(obj);
     }
 
+    @Override
+    public void insertUserInfoBatch(List<SaveUserInfoByProjectDTO> list) {
+        for (SaveUserInfoByProjectDTO dto : list) {
+            Long jlzyContractId = selectIsRecordJlId(dto.getContractId());
+            if(jlzyContractId!=null){
+                dto.setIsRecordJlId(jlzyContractId);
+            }
+        }
+        saveUserInfoByProjectService.saveBatch(list);
+    }
+
+    @Override
+    public void updateUserInfoBatch(List<SaveUserInfoByProjectDTO> updateUserInfoByProjectList) {
+        saveUserInfoByProjectService.updateBatchById(updateUserInfoByProjectList);
+    }
+
+    public Long selectIsRecordJlId(String contractId){
+        String sql="select contract_id_jlyz from m_contract_relation_jlyz where contract_id_sg="+contractId;
+        List<Long> list = jdbcTemplate.query(sql, new SingleColumnRowMapper<>(Long.class));
+        if(list.isEmpty()){
+            return null;
+        }else {
+            return list.get(0);
+        }
+    }
+
 
 }

+ 2 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractInfoMapper.java

@@ -77,4 +77,6 @@ public interface ContractInfoMapper extends BaseMapper<ContractInfo> {
     List<RoleSignPfxUserVO1> findAllUserAndRoleList1(Long contractId, Long roleId);
 
     List<RoleSignPfxUserVO2> findAllUserAndRoleList2(Long contractId, Long roleId);
+
+    List<ContractInfo> queryContractNamesListByProjectId(@Param("projectId") String projectId);
 }

+ 10 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractInfoMapper.xml

@@ -585,5 +585,15 @@
             And r.id=#{roleId}
         </if>
     </select>
+    <select id="queryContractNamesListByProjectId" resultType="org.springblade.manager.entity.ContractInfo">
+        SELECT
+        *
+        FROM
+        m_contract_info
+        WHERE
+        is_deleted = 0
+        AND p_id = #{projectId}
+    </select>
+
 
 </mapper>

+ 3 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ProjectInfoMapper.java

@@ -10,6 +10,7 @@ import org.springblade.manager.vo.ProjectUserAmountVO;
 import org.springblade.manager.vo.SingPfxManagementVO;
 
 import java.util.List;
+import java.util.Set;
 
 public interface ProjectInfoMapper extends BaseMapper<ProjectInfo> {
 
@@ -32,4 +33,6 @@ public interface ProjectInfoMapper extends BaseMapper<ProjectInfo> {
     ProjectInfo selectOneAndWbsTypeById(Long id);
 
     IPage<ProjectInfoVO> pageList(IPage<ProjectInfoVO> page, @Param("vo") ProjectInfoVO3 vo);
+
+    List<String> queryProjectIds(@Param("projectIds") Set<String> projectIds);
 }

+ 9 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ProjectInfoMapper.xml

@@ -191,5 +191,14 @@
             </when>
         </choose>
     </select>
+    <select id="queryProjectIds" resultType="java.lang.String">
+        select id from m_project_info where is_deleted = 0
+        <if test="projectIds != null">
+            and id in
+            <foreach item="projectId" collection="projectIds" open="(" separator="," close=")">
+                #{projectId}
+            </foreach>
+        </if>
+    </select>
 
 </mapper>

+ 2 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractInfoService.java

@@ -91,4 +91,6 @@ public interface IContractInfoService extends BaseService<ContractInfo> {
 
     R<Object> getCollectTreeNode(String contractId, String tableOwner, Long folderId, String queryValue, Long pKeyId);
     R<Object> getCollectTreeNodeByQuery(String contractId, String tableOwner, Long folderId, String queryValue);
+
+    List<ContractInfo> queryContractNamesListByProjectId(String projectId);
 }

+ 5 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IProjectInfoService.java

@@ -6,6 +6,7 @@ import org.springblade.core.mp.base.BaseService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 
 import java.util.List;
+import java.util.Set;
 
 public interface IProjectInfoService extends BaseService<ProjectInfo> {
 
@@ -15,6 +16,8 @@ public interface IProjectInfoService extends BaseService<ProjectInfo> {
 
     List<ProjectInfo> selectProjectList(List<String> projectIds);
 
+    List<ProjectInfo> selectProjectList(Set<String> projectIds);
+
     IPage<ProjectInfoVO> selectProjectInfoPage(IPage<ProjectInfoVO> page, ProjectInfoVO projectInfo);
 
     List<ContractlnfoCountVO> selectContractInfoCount();
@@ -28,4 +31,6 @@ public interface IProjectInfoService extends BaseService<ProjectInfo> {
     Long getProjectIdbyName(String projectName);
 
     IPage<ProjectInfoVO> pageList(IPage<ProjectInfoVO> page, ProjectInfoVO3 vo);
+
+    List<String> queryProjectIds(Set<String> projectId);
 }

+ 5 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java

@@ -1849,4 +1849,9 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
         }
         return null;
     }
+
+    @Override
+    public List<ContractInfo> queryContractNamesListByProjectId(String projectId) {
+        return contractInfoMapper.queryContractNamesListByProjectId(projectId);
+    }
 }

+ 14 - 4
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ProjectInfoServiceImpl.java

@@ -14,16 +14,14 @@ import org.springblade.manager.mapper.*;
 import org.springblade.manager.service.IProjectInfoService;
 import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.manager.wrapper.ProjectInfoWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 
 import javax.annotation.Resource;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -41,6 +39,8 @@ public class ProjectInfoServiceImpl extends BaseServiceImpl<ProjectInfoMapper, P
 
     @Resource
     private JdbcTemplate jdbcTemplate;
+    @Autowired
+    private ProjectInfoMapper projectInfoMapper;
 
     public List<ProjectUserAmountVO> queryProjectUserAmount() {
         return this.baseMapper.queryProjectUserAmount();
@@ -85,6 +85,11 @@ public class ProjectInfoServiceImpl extends BaseServiceImpl<ProjectInfoMapper, P
         return this.baseMapper.selectProjectList(projectIds);
     }
 
+    @Override
+    public List<ProjectInfo> selectProjectList(Set<String> projectIds) {
+        return this.baseMapper.selectProjectList((List<String>) projectIds);
+    }
+
     @Override
     public IPage<ProjectInfoVO> selectProjectInfoPage(IPage<ProjectInfoVO> page, ProjectInfoVO projectInfo) {
         return page.setRecords(baseMapper.selectProjectInfoPage(page, projectInfo));
@@ -186,4 +191,9 @@ public class ProjectInfoServiceImpl extends BaseServiceImpl<ProjectInfoMapper, P
         return pagedList;
     }
 
+    @Override
+    public List<String> queryProjectIds(Set<String> projectId) {
+        return projectInfoMapper.queryProjectIds(projectId);
+    }
+
 }

+ 37 - 5
blade-service/blade-user/src/main/java/org/springblade/system/user/controller/UserController.java

@@ -17,6 +17,8 @@
 package org.springblade.system.user.controller;
 
 
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.read.metadata.ReadSheet;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.nacos.common.utils.MD5Utils;
@@ -26,6 +28,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.annotations.*;
 import lombok.AllArgsConstructor;
+import org.apache.commons.codec.Charsets;
 import org.apache.http.Consts;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
@@ -37,6 +40,8 @@ import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.message.BasicNameValuePair;
 import org.springblade.common.cache.CacheNames;
 import org.springblade.common.constant.CommonConstant;
+import org.springblade.common.utils.CommonUtil;
+import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.cache.utils.CacheUtil;
 import org.springblade.core.excel.util.ExcelUtil;
 import org.springblade.core.mp.support.Condition;
@@ -56,8 +61,7 @@ import org.springblade.system.cache.ParamCache;
 import org.springblade.system.user.bean.CyGetToken;
 import org.springblade.system.user.bean.ResultCYData;
 import org.springblade.system.user.bean.ResultCYKey;
-import org.springblade.system.user.dto.TokenVerifyResult;
-import org.springblade.system.user.dto.UserDTO;
+import org.springblade.system.user.dto.*;
 import org.springblade.system.user.entity.User;
 import org.springblade.system.user.excel.UserExcel;
 import org.springblade.system.user.excel.UserExcel2;
@@ -75,9 +79,10 @@ import springfox.documentation.annotations.ApiIgnore;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
+import java.net.URLEncoder;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
@@ -354,6 +359,15 @@ public class UserController {
         return R.success("操作成功");
     }
 
+    @PostMapping("import-user_new")
+    @ApiOperationSupport(order = 12)
+    @ApiOperation(value = "导入用户", notes = "传入excel(多sheet+纵向合同段逗号拼接+过滤全null行)")
+    public R importUserNew(@RequestParam("file") MultipartFile file) {
+        return userService.importUserNew(file);
+    }
+
+
+
     /**
      * 导出全部用户
      */
@@ -377,6 +391,24 @@ public class UserController {
         ExcelUtil.export(response, "用户数据模板" + DateUtil.time(), "用户数据表", list, UserExcel.class);
     }
 
+    @GetMapping("/downloadImportUserExcel")
+    @ApiOperationSupport(order = 31)
+    @ApiOperation(value = "下载excel数据")
+    @ApiImplicitParam(name = "fileId", value = "fileId")
+    public void downloadImportUserExcel(HttpServletResponse response) throws Exception {
+        String fileName = URLEncoder.encode("用户数据模版", Charsets.UTF_8.name());
+        String filePath="https://xinan1.zos.ctyun.cn/blade-oss-chongqing/upload/20251203/f42a9c6956903e8bd3d9559ffcaf1326.xlsx";
+        InputStream redio = CommonUtil.getOSSInputStream(filePath);
+        byte[] buffer = IoUtil.readToByteArray(redio);
+        OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
+        response.setContentType("application/vnd.ms-excel");
+        response.setCharacterEncoding(org.apache.commons.codec.Charsets.UTF_8.name());
+        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+        toClient.write(buffer);
+        toClient.flush();
+        toClient.close();
+    }
+
 
     /**
      * 第三方注册用户

+ 3 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/service/IUserService.java

@@ -20,6 +20,7 @@ import org.springblade.system.user.excel.UserExcel;
 import org.springblade.system.user.vo.InformationQueryBIMVO;
 import org.springblade.system.user.vo.InformationQueryVO1;
 import org.springblade.system.user.vo.UserVO;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -248,4 +249,6 @@ public interface IUserService extends BaseService<User> {
     List<InformationQueryVO1> queryInformationByContractIdAndName(String contractId, String name);
 
     R queryInformationByFileNumber(String contractId, String fieldNumber);
+
+    R importUserNew(MultipartFile file);
 }

+ 815 - 1
blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -1,10 +1,13 @@
 package org.springblade.system.user.service.impl;
 
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.read.metadata.ReadSheet;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.nacos.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -35,6 +38,8 @@ import org.springblade.core.tool.utils.*;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.entity.*;
 import org.springblade.manager.feign.ContractClient;
+import org.springblade.manager.feign.ProjectAssignmentUserClient;
+import org.springblade.manager.feign.SaveUserInfoByProjectClient;
 import org.springblade.manager.vo.PrivateTreeVO;
 import org.springblade.manager.vo.PrivateTreeVO2;
 import org.springblade.manager.vo.WbsTreeContractLazyQueryInfoVO;
@@ -45,12 +50,13 @@ import org.springblade.system.cache.DictCache;
 import org.springblade.system.cache.ParamCache;
 import org.springblade.system.cache.SysCache;
 import org.springblade.system.entity.Dept;
+import org.springblade.system.entity.Role;
 import org.springblade.system.entity.Tenant;
 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.UserDTO;
+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;
@@ -69,15 +75,24 @@ import org.springblade.system.user.wrapper.UserWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.SingleColumnRowMapper;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -85,6 +100,7 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -115,6 +131,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
     private final JdbcTemplate jdbcTemplate;
     @Autowired
     StringRedisTemplate redisTemplate;
+    @Autowired
+    private SaveUserInfoByProjectClient saveUserInfoByProjectClient;
 
 
     @Override
@@ -2358,4 +2376,800 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
         return R.data(information);
     }
 
+    @Override
+    @Transactional
+    public R importUserNew(MultipartFile file) {
+        List<UserImporterNew> finalUserList = new ArrayList<>();
+        List<ImportUserLog>logs=new ArrayList<>();
+        LocalDateTime now = LocalDateTime.now();
+        String importTime = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        Long importId= SnowFlakeUtil.getId();
+        Long importUser=AuthUtil.getUserId();
+        List<User> insertUserList=new ArrayList<>();
+        List<UserDept> insertUserDeptList=new ArrayList<>();
+        List<SaveUserInfoByProjectDTO> insertUserInfoByProjectList=new ArrayList<>();
+        List<User>updateUserList=new ArrayList<>();
+        List<SaveUserInfoByProjectDTO>updateUserInfoByProjectList=new ArrayList<>();
+        try {
+            // 1. 获取所有sheet元数据
+            List<ReadSheet> allSheets = EasyExcel.read(file.getInputStream())
+                    .head(ExcelUserTemp.class)
+                    .build()
+                    .excelExecutor()
+                    .sheetList();
+            // 遍历每个Sheet,逐个读取并处理数据
+            for (ReadSheet sheet : allSheets) {
+                System.out.printf("开始读取Sheet:%s(索引:%d)%n", sheet.getSheetName(), sheet.getSheetNo());
+
+                List<ExcelUserTemp> sheetData = EasyExcel.read(file.getInputStream())
+                        .head(ExcelUserTemp.class)
+                        .sheet(sheet.getSheetNo())
+                        .headRowNumber(2)
+                        .doReadSync();
+
+                UserImporterNew currentUser = null;
+                List<ImportPorjectInfoDTO> tempProjectList = new ArrayList<>();
+                String currentProjectId = ""; // 新增:存储当前用户的“基准项目ID”(继承自新用户行)
+
+                for (ExcelUserTemp row : sheetData) {
+                    if (!isCoreFieldEmpty(row)) {
+                        // 处理上一个用户
+                        if (currentUser != null) {
+                            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);
+                        }
+                    } else {
+                        // 补充行:继承当前基准项目ID
+                        if (currentUser != null) {
+                            // 若补充行有新的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) {
+                    currentUser.setProjectInfoList(tempProjectList);
+                    finalUserList.add(currentUser);
+                }
+            }
+            String file_path = FileUtils.getSysLocalFileUrl();
+            String filePath = file_path + "/importExcel/" + importId + ".xlsx";
+            try {
+                // 创建目录(如果不存在)
+                File directory = new File(file_path + "/importExcel/");
+                if (!directory.exists()) {
+                    directory.mkdirs();
+                }
+                // 保存文件
+                file.transferTo(new File(filePath));
+            } catch (IOException e) {
+                // 处理异常
+                throw new ServiceException("文件保存失败: " + e.getMessage());
+            }
+            // 5. 后续业务逻辑(批量保存等)
+            if (!finalUserList.isEmpty()) {
+                //查出所有的部门名
+                List<Dept>deptNames=jdbcTemplate.query("select id,dept_name from blade_dept where is_deleted=0",new BeanPropertyRowMapper<>(Dept.class));
+                //查出所有的角色名
+                List<Role>roleNamesSG=jdbcTemplate.query("select id,role_name from blade_role where parent_id=(select id from blade_role where role_name='施工方' and is_deleted=0 limit 1)",new BeanPropertyRowMapper<>(Role.class));
+                List<Role>roleNamesJL=jdbcTemplate.query("select id,role_name from blade_role where parent_id=(select id from blade_role where role_name='监理方' and is_deleted=0 limit 1)",new BeanPropertyRowMapper<>(Role.class));
+                List<Role>roleNamesYZ=jdbcTemplate.query("select id,role_name from blade_role where parent_id=(select id from blade_role where role_name='业主方' and is_deleted=0 limit 1)",new BeanPropertyRowMapper<>(Role.class));
+                Map<String,List<Role>> roleMap= new HashMap<>();
+                roleMap.put("3",roleNamesSG);
+                roleMap.put("2",roleNamesJL);
+                roleMap.put("1",roleNamesYZ);
+                //查出所有的项目的合同段名
+                // 查出所有的项目的合同段名
+                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()){
+                    for (String projectId : projectIds) {
+                      List<ContractInfo>contractNames=contractClient.queryContractNamesListByProjectId(projectId);
+                      contractNamesMap.put(projectId,contractNames);
+                    }
+                }
+                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())){
+                        //姓名不为null
+                        if(!StringUtil.isEmpty(userImporterNew.getRealName())){
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),userImporterNew.getRealName()+"的账号为空",importTime,importId,importUser,file.getOriginalFilename());
+                            logs.add(log);
+                        }else {
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"账号姓名都为空",importTime,importId,importUser,filePath);
+                            logs.add(log);
+                        }
+                        continue;
+                    }
+                    User user=this.getOne(new QueryWrapper<User>().lambda().eq(User::getAccount,userImporterNew.getAccount()));
+                    //账号不存在 则新增
+                    if(user==null){
+                        //前置条件判断
+                        /**
+                         * 验证姓名
+                         */
+                        if(StringUtil.isEmpty(userImporterNew.getRealName())){
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"姓名为空",importTime,importId,importUser,filePath);
+                            logs.add(log);
+                            continue;
+                        }
+                        /**
+                         * 验证手机号
+                         */
+                        if(StringUtil.isEmpty(userImporterNew.getPhone())){
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"手机号为空",importTime,importId,importUser,filePath);
+                            logs.add(log);
+                            continue;
+                        }else {
+                            boolean b = isValidChinesePhoneNumber(userImporterNew.getPhone());
+                            if(!b){
+                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"手机号格式错误",importTime,importId,importUser,filePath);
+                                logs.add(log);
+                                continue;
+                            }
+                        }
+                        /**
+                         * 验证身份证号
+                         */
+                        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);
+                                logs.add(log);
+                                continue;
+                            }
+                        }
+                        /**
+                         * 验证用户平台
+                         */
+                        if(StringUtil.isEmpty(userImporterNew.getUserType())){
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"用户平台为空",importTime,importId,importUser,filePath);
+                            logs.add(log);
+                            continue;
+                        }else {
+                            String userType = userImporterNew.getUserType();
+                            List<String> types = new ArrayList<>();
+                            if (userType != null && !userType.isEmpty()) {
+                                Pattern pattern = Pattern.compile("\\d+");
+                                java.util.regex.Matcher matcher = pattern.matcher(userType);
+                                while (matcher.find()) {
+                                    types.add(matcher.group());
+                                }
+                            }
+                            //判断是否只包含1,2,3,7
+                            Set<String> allowedTypes = new HashSet<>(Arrays.asList("1", "2", "3", "7"));
+                            boolean isValid = types.stream().allMatch(allowedTypes::contains);
+                            if(!isValid){
+                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"用户平台格式错误",importTime,importId,importUser,filePath);
+                                logs.add(log);
+                                continue;
+                            }else {
+                                userImporterNew.setUserType(String.join(",", types));
+                            }
+                        }
+                        /**
+                         * 验证部门
+                         */
+                        if(StringUtil.isEmpty(userImporterNew.getDeptName())){
+                            ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属部门为空",importTime,importId,importUser,filePath);
+                            logs.add(log);
+                            continue;
+                        }else {
+                            Optional<Dept> dept = deptNames.stream().filter(deptName -> !StringUtil.isEmpty(deptName.getDeptName()) && deptName.getDeptName().equals(userImporterNew.getDeptName())).findFirst();
+                            if(!dept.isPresent()){
+                                ImportUserLog log=new ImportUserLog(SnowFlakeUtil.getId(),userImporterNew.getAccount(),userImporterNew.getRealName(),"所属部门不存在",importTime,importId,importUser,filePath);
+                                logs.add(log);
+                                continue;
+                            }else {
+                                userImporterNew.setDeptId(dept.get().getId());
+                            }
+                        }
+                        //如果部门信息全都为null 就跳过验证
+                        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);
+                                        dto.setStatus(1);
+                                        continue;
+                                    }
+                                }
+                                /**
+                                 * 验证合同段
+                                 */
+                                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(),"合同段名称/编号不存在:"+dto.getContractInfo(),importTime,importId,importUser,filePath);
+                                        logs.add(log);
+                                        dto.setStatus(1);
+                                        continue;
+                                    }else {
+                                        if(nameOptional.isPresent()){
+                                            dto.setContractId(nameOptional.get().getId());
+                                        }else {
+                                            dto.setContractId(numberOptional.get().getId());
+                                        }
+                                    }
+                                }
+                                /**
+                                 * 验证所属方
+                                 */
+                                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(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(","));
+                            }
+                        }
+                        /**
+                         * 用户账号表
+                         */
+                        User insertUser=new User();
+                        insertUser.setId(SnowFlakeUtil.getId());
+                        insertUser.setTenantId(AuthUtil.getTenantId());
+                        insertUser.setAccount(userImporterNew.getAccount());
+                        insertUser.setUserType(userImporterNew.getUserType());
+                        insertUser.setPassword(DigestUtil.encrypt(userImporterNew.getPassword()));
+                        insertUser.setPlaintextPassword(userImporterNew.getPassword());
+                        insertUser.setName(userImporterNew.getRealName());
+                        insertUser.setRealName(userImporterNew.getRealName());
+                        insertUser.setIdNumber(userImporterNew.getIdNumber());
+                        insertUser.setPhone(userImporterNew.getPhone());
+                        insertUser.setRoleId(roleIds);
+                        insertUser.setDeptId(userImporterNew.getDeptId()+"");
+                        insertUser.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
+                        insertUser.setAccCode(userImporterNew.getAccCode());
+                        insertUserList.add(insertUser);
+                        /**
+                         * 用户部门表
+                         */
+                        UserDept userDept = new UserDept();
+                        userDept.setUserId(insertUser.getId());
+                        userDept.setDeptId(userImporterNew.getDeptId());
+                        insertUserDeptList.add(userDept);
+                        /**
+                         * 项目分配用户关系表
+                         */
+                        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 {
+                        //账号存在 则更新
+                        //模板上姓名和数据库的姓名相同则修改
+                        if(!StringUtil.isEmpty(userImporterNew.getRealName())){
+                           if(user.getRealName().equals(userImporterNew.getRealName())){
+                               //拿到项目和合同段
+                               //如果部门信息全都为null 就跳过验证
+                               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);
+                                               dto.setStatus(1);
+                                               continue;
+                                           }
+                                       }
+                                       /**
+                                        * 验证合同段
+                                        */
+                                       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(),"合同段名称/编号不存在:"+dto.getContractInfo(),importTime,importId,importUser,filePath);
+                                               logs.add(log);
+                                               dto.setStatus(1);
+                                               continue;
+                                           }else {
+                                               if(nameOptional.isPresent()){
+                                                   dto.setContractId(nameOptional.get().getId());
+                                               }else {
+                                                   dto.setContractId(numberOptional.get().getId());
+                                               }
+                                           }
+                                       }
+                                       /**
+                                        * 验证所属方
+                                        */
+                                       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(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() != 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);
+                                           user.setRoleId(user.getRoleId()+","+dto.getRoleId());
+                                           user.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
+                                           updateUserList.add(user);
+                                       } else {
+                                           SaveUserInfoByProjectDTO projectdto = query.get(0);
+                                           projectdto.setRoleId(dto.getRoleId() + "");
+                                           updateUserInfoByProjectList.add(projectdto);
+                                           user.setRoleId(user.getRoleId()+","+dto.getRoleId());
+                                           user.setStatus("是".equals(userImporterNew.getStatus()) ? 1 : 0);
+                                           updateUserList.add(user);
+                                       }
+                                   }
+                               }
+                           }
+                        }
+                    }
+                }
+                if(!insertUserList.isEmpty()){
+                    this.saveBatch(insertUserList);
+                }
+                if(!insertUserDeptList.isEmpty()){
+                    userDeptService.saveBatch(insertUserDeptList);
+                }
+                if(!insertUserInfoByProjectList.isEmpty()){
+                    saveUserInfoByProjectClient.insertUserInfoBatch(insertUserInfoByProjectList);
+                }
+                if(!updateUserInfoByProjectList.isEmpty()){
+                    saveUserInfoByProjectClient.updateUserInfoBatch(updateUserInfoByProjectList);
+                }
+                if(!updateUserList.isEmpty()){
+                    for (User user : updateUserList) {
+                        String sql="select role_id from m_project_assignment_user where user_id="+user.getId()+" and is_deleted=0";
+                        List<String> roleIds = jdbcTemplate.query(sql, new SingleColumnRowMapper<>(String.class));
+                        String roleId = user.getRoleId();
+                        if (!StringUtil.isEmpty(roleId)) {
+                            // 按逗号分割,去重后再重新拼接
+                            List<String> roleIdList = Arrays.asList(roleId.split(","));
+                            List<String> distinctRoleIds = roleIdList.stream().distinct().collect(Collectors.toList());
+                            roleId = String.join(",", distinctRoleIds);
+                        }
+                        user.setRoleId(roleIds.isEmpty() ? roleId : String.join(",", roleIds));
+                    }
+                    this.updateBatchById(updateUserList);
+                }
+                if(!logs.isEmpty()){
+                    this.saveUserImportLogs(logs);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail("导入失败:" + e.getMessage());
+        }
+        if(logs.size()>0){
+            return R.fail("导入失败:" + logs.size() + "条记录导入失败");
+        }
+        return R.success("导入成功,共新增:" + insertUserList.size() + "条有效记录,更新:"+updateUserInfoByProjectList.size()+"条记录");
+    }
+
+    /**
+     * 保存用户导入日志
+     * @param logs
+     */
+    private void saveUserImportLogs(List<ImportUserLog> logs) {
+        if (logs == null || logs.isEmpty()) {
+            return;
+        }
+        String sql = "INSERT INTO blade_import_user_log " +
+                "(id, account, name, message, import_time, import_id, import_user, file_path) " +
+                "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
+            @Override
+            public void setValues(PreparedStatement ps, int i) throws SQLException {
+                ImportUserLog log = logs.get(i);
+                ps.setObject(1, log.getId());            // id
+                ps.setString(2, log.getAccount());       // account
+                ps.setString(3, log.getName());          // name
+                ps.setString(4, log.getMessage());       // message
+                ps.setString(5, log.getImportTime());    // import_time
+                ps.setObject(6, log.getImportId());      // import_id
+                ps.setObject(7, log.getImportUser());    // import_user
+                ps.setString(8, log.getFilePath());      // file_path
+            }
+            @Override
+            public int getBatchSize() {
+                return logs.size();
+            }
+        });
+    }
+
+    /**
+     * 初始化UserImporterNew:赋值核心字段+处理默认值
+     * @param row Excel行数据
+     * @return 初始化后的UserImporterNew
+     */
+    private UserImporterNew initUserImporterNew(ExcelUserTemp row) {
+        UserImporterNew user = new UserImporterNew();
+        // 处理默认值:密码默认"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(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);
+        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 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());
+    }
+    /**
+     * 判断中国大陆手机号格式是否正确
+     *
+     * @param phoneNumber 手机号
+     * @return true表示格式正确,false表示格式错误
+     */
+    public static boolean isValidChinesePhoneNumber(String phoneNumber) {
+        if (phoneNumber == null || phoneNumber.length() != 11) {
+            return false;
+        }
+
+        // 使用正则表达式匹配手机号格式
+        String regex = "^1[3-9]\\d{9}$";
+        return phoneNumber.matches(regex);
+    }
+
+    /**
+     * 判断身份证格式是否正确
+     *
+     * @param idCard 身份证号码
+     * @return true表示格式正确,false表示格式错误
+     */
+    public static boolean isValidIdCard(String idCard) {
+        if (idCard == null || idCard.trim().isEmpty()) {
+            return false;
+        }
+
+        idCard = idCard.trim();
+
+        // 长度验证
+        if (idCard.length() == 15) {
+            return isValidIdCard15(idCard);
+        } else if (idCard.length() == 18) {
+            return isValidIdCard18(idCard);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 验证15位身份证号码
+     *
+     * @param idCard 15位身份证号码
+     * @return true表示格式正确,false表示格式错误
+     */
+    private static boolean isValidIdCard15(String idCard) {
+        // 15位身份证号码为纯数字
+        if (!Pattern.matches("\\d{15}", idCard)) {
+            return false;
+        }
+
+        // 验证出生日期
+        String birthDay = "19" + idCard.substring(6, 12); // 15位身份证年份默认为19XX年
+        return isValidDate(birthDay, "yyyyMMdd");
+    }
+
+    /**
+     * 验证18位身份证号码
+     *
+     * @param idCard 18位身份证号码
+     * @return true表示格式正确,false表示格式错误
+     */
+    private static boolean isValidIdCard18(String idCard) {
+        // 18位身份证号码前17位为数字,最后一位为数字或X
+        if (!Pattern.matches("\\d{17}[\\dXx]", idCard)) {
+            return false;
+        }
+
+        // 验证地址码(简单验证前两位)
+        String areaCode = idCard.substring(0, 2);
+        if (!AREA_CODES.contains(areaCode)) {
+            return false;
+        }
+
+        // 验证出生日期
+        String birthDay = idCard.substring(6, 14);
+        if (!isValidDate(birthDay, "yyyyMMdd")) {
+            return false;
+        }
+
+        // 验证校验码
+        return checkIdCard18(idCard);
+    }
+
+    /**
+     * 验证日期格式是否正确
+     *
+     * @param date   日期字符串
+     * @param format 日期格式
+     * @return true表示日期格式正确,false表示日期格式错误
+     */
+    private static boolean isValidDate(String date, String format) {
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        sdf.setLenient(false); // 严格模式,不允许不合理的日期
+        try {
+            sdf.parse(date);
+            // 检查年份范围(例如:1900年至今)
+            Calendar calendar = new GregorianCalendar();
+            calendar.setTime(sdf.parse(date));
+            int year = calendar.get(Calendar.YEAR);
+            int currentYear = Calendar.getInstance().get(Calendar.YEAR);
+            return year >= 1900 && year <= currentYear;
+        } catch (ParseException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 验证18位身份证号码的校验码
+     *
+     * @param idCard 18位身份证号码
+     * @return true表示校验码正确,false表示校验码错误
+     */
+    private static boolean checkIdCard18(String idCard) {
+        char[] idCardArray = idCard.toCharArray();
+        int[] weight = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
+        char[] validate = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
+        int sum = 0;
+
+        for (int i = 0; i < 17; i++) {
+            sum += (idCardArray[i] - '0') * weight[i];
+        }
+
+        int mod = sum % 11;
+        char validateCode = validate[mod];
+
+        return Character.toUpperCase(idCardArray[17]) == validateCode;
+    }
+
+    // 简化的省份代码列表(前两位)
+    private static final java.util.Set<String> AREA_CODES = new java.util.HashSet<>();
+    static {
+        AREA_CODES.add("11"); // 北京
+        AREA_CODES.add("12"); // 天津
+        AREA_CODES.add("13"); // 河北
+        AREA_CODES.add("14"); // 山西
+        AREA_CODES.add("15"); // 内蒙古
+        AREA_CODES.add("21"); // 辽宁
+        AREA_CODES.add("22"); // 吉林
+        AREA_CODES.add("23"); // 黑龙江
+        AREA_CODES.add("31"); // 上海
+        AREA_CODES.add("32"); // 江苏
+        AREA_CODES.add("33"); // 浙江
+        AREA_CODES.add("34"); // 安徽
+        AREA_CODES.add("35"); // 福建
+        AREA_CODES.add("36"); // 江西
+        AREA_CODES.add("37"); // 山东
+        AREA_CODES.add("41"); // 河南
+        AREA_CODES.add("42"); // 湖北
+        AREA_CODES.add("43"); // 湖南
+        AREA_CODES.add("44"); // 广东
+        AREA_CODES.add("45"); // 广西
+        AREA_CODES.add("46"); // 海南
+        AREA_CODES.add("50"); // 重庆
+        AREA_CODES.add("51"); // 四川
+        AREA_CODES.add("52"); // 贵州
+        AREA_CODES.add("53"); // 云南
+        AREA_CODES.add("54"); // 西藏
+        AREA_CODES.add("61"); // 陕西
+        AREA_CODES.add("62"); // 甘肃
+        AREA_CODES.add("63"); // 青海
+        AREA_CODES.add("64"); // 宁夏
+        AREA_CODES.add("65"); // 新疆
+        AREA_CODES.add("71"); // 台湾
+        AREA_CODES.add("81"); // 香港
+        AREA_CODES.add("82"); // 澳门
+        AREA_CODES.add("91"); // 国外
+    }
+
+
 }