| | |
| | | @PostMapping("/importUser") |
| | | public R<Object> importUser(@RequestPart("file") MultipartFile file) { |
| | | ImportParams params = new ImportParams(); |
| | | params.setHeadRows(1); // 表头行数 |
| | | params.setHeadRows(2); // 表头行数改为2行 |
| | | InputStream inputStream = null; |
| | | List<EmployeeImportExcel> employeeExcelList; |
| | | |
| | | Map<String, SysUser> userMap = userService.selectAllList().stream() |
| | | .collect(Collectors.toMap(SysUser::getPhonenumber, e -> e)); |
| | | |
| | | try { |
| | | inputStream = file.getInputStream(); |
| | | employeeExcelList = ExcelImportUtil.importExcel(inputStream, |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (CollectionUtils.isEmpty(employeeExcelList)) { |
| | | throw new ServiceException("员工数据为空!"); |
| | | } |
| | | |
| | | |
| | | int successCount = 0; |
| | | int failCount = 0; |
| | | List<String> errorMessages = new ArrayList<>(); |
| | | |
| | | Map<String, TDept> deptMap = tDeptService.list().stream().collect(Collectors.toMap(TDept::getDeptName, e -> e)); |
| | | Map<String, SysRole> roleMap = roleService.selectRoleAll().stream().collect(Collectors.toMap(SysRole::getRoleName, e -> e)); |
| | | for (EmployeeImportExcel employeeExcel : employeeExcelList) { |
| | | try { |
| | | // 数据验证 |
| | | |
| | | |
| | | // 数据验证 |
| | | if (StringUtils.isEmpty(employeeExcel.getNickName())) { |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:姓名为空"); |
| | | failCount++; |
| | | continue; |
| | | } |
| | | |
| | | |
| | | if (StringUtils.isEmpty(employeeExcel.getPhoneNumber())) { |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:联系电话为空"); |
| | | failCount++; |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 转换并保存用户 |
| | | SysUser sysUser = convertToSysUser(employeeExcel); |
| | | if (StringUtils.isEmpty(employeeExcel.getDepartment())) { |
| | |
| | | continue; |
| | | } |
| | | |
| | | TDept dept = tDeptService.lambdaQuery().eq(TDept::getDeptName, employeeExcel.getDepartment()) |
| | | .last("limit 1").one(); |
| | | TDept dept = deptMap.get(employeeExcel.getDepartment().trim()); |
| | | if (dept == null){ |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:所在股室不存在"); |
| | | failCount++; |
| | | continue; |
| | | } |
| | | SysRole role = roleMap.get(employeeExcel.getRoleName().trim()); |
| | | if (role == null){ |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:角色不存在"); |
| | | failCount++; |
| | | continue; |
| | | } |
| | | SysUser sysUser1 = userMap.get(sysUser.getPhonenumber()); |
| | | if (sysUser1 != null){ |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:电话号码已存在"); |
| | | failCount++; |
| | | continue; |
| | | } |
| | | sysUser.setRoleId(role.getRoleId()); |
| | | sysUser.setDeptId(dept.getId().toString()); |
| | | userService.insertUser(sysUser); |
| | | successCount++; |
| | | |
| | | |
| | | } catch (Exception e) { |
| | | errorMessages.add("第" + (successCount + failCount + 1) + "行:" + e.getMessage()); |
| | | failCount++; |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("successCount", successCount); |
| | | result.put("failCount", failCount); |
| | | result.put("totalCount", successCount + failCount); |
| | | if (!errorMessages.isEmpty()) { |
| | | result.put("errorMessages", errorMessages); |
| | | return R.fail(result.get("errorMessages")); |
| | | return R.fail("部分数据导入失败:"+result.get("errorMessages").toString()); |
| | | } |
| | | |
| | | |
| | | return R.ok("导入完成", result.toString()); |
| | | } |
| | | |
| | | /** |
| | | * 解析日期字符串为Date对象 |
| | | * @param dateStr 日期字符串,格式如:2022.09 |
| | | * @return Date对象 |
| | | */ |
| | | private Date parseDate(String dateStr) { |
| | | if (StringUtils.isEmpty(dateStr)) { |
| | | return null; |
| | | } |
| | | |
| | | @ApiOperation(value = "员工导入模板下载") |
| | | @GetMapping("/importDownload") |
| | | @CrossOrigin(origins = "*") |
| | | public void importDownload(HttpServletResponse response) { |
| | | ServletOutputStream outputStream = null; |
| | | Workbook workbook = null; |
| | | |
| | | try { |
| | | // 支持 YYYY.MM 格式 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); |
| | | return sdf.parse(dateStr); |
| | | } catch (ParseException e) { |
| | | // 确保在创建workbook之前没有输出任何内容 |
| | | response.reset(); |
| | | |
| | | // 创建工作簿 |
| | | workbook = new HSSFWorkbook(); |
| | | Sheet sheet = workbook.createSheet("员工信息"); |
| | | |
| | | // 创建样式 |
| | | CellStyle headStyle = workbook.createCellStyle(); |
| | | headStyle.setAlignment(HorizontalAlignment.CENTER); |
| | | headStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | headStyle.setBorderBottom(BorderStyle.THIN); |
| | | headStyle.setBorderTop(BorderStyle.THIN); |
| | | headStyle.setBorderLeft(BorderStyle.THIN); |
| | | headStyle.setBorderRight(BorderStyle.THIN); |
| | | |
| | | Font font = workbook.createFont(); |
| | | font.setBold(true); |
| | | headStyle.setFont(font); |
| | | |
| | | // 创建两行 |
| | | Row row1 = sheet.createRow(0); |
| | | Row row2 = sheet.createRow(1); |
| | | |
| | | // 设置列宽 |
| | | for (int i = 0; i < 20; i++) { |
| | | sheet.setColumnWidth(i, 15 * 256); |
| | | } |
| | | |
| | | // 根据截图重新设计列结构 |
| | | // A-H列(0-7): 基础信息 |
| | | // I-N列(8-13): 学历相关 |
| | | // O-S列(14-18): 其他信息 |
| | | |
| | | // 第一行表头 - 20列 |
| | | String[] firstRowHeaders = { |
| | | "序号", "姓名", "性别", "身份证号码", "年龄", "政治面貌", |
| | | "参工时间", "进入公司时间", "", "", "", "", "", "", // 移除重复的"学历"字段 |
| | | "所在股室", "职务", "任职时间", "电话号码", "编制","角色" |
| | | }; |
| | | |
| | | // 第二行表头 - 20列 |
| | | String[] secondRowHeaders = { |
| | | "", "", "", "", "", "", "", "", |
| | | "第一学历", "第一学历-学院", "第一学历-专业", "最高学历", "最高学历-学院", "最高学历-专业", "", |
| | | "", "", "", "","" |
| | | }; |
| | | |
| | | // 填充第一行 |
| | | for (int i = 0; i < firstRowHeaders.length; i++) { |
| | | Cell cell = row1.createCell(i); |
| | | cell.setCellValue(firstRowHeaders[i]); |
| | | cell.setCellStyle(headStyle); |
| | | } |
| | | |
| | | // 填充第二行 |
| | | for (int i = 0; i < secondRowHeaders.length; i++) { |
| | | Cell cell = row2.createCell(i); |
| | | cell.setCellValue(secondRowHeaders[i]); |
| | | cell.setCellStyle(headStyle); |
| | | } |
| | | |
| | | // 合并单元格操作 |
| | | // 1. 先合并"学历"大标题区域(第8列到第13列,只合并第一行) |
| | | sheet.addMergedRegion(new CellRangeAddress(0, 0, 8, 13)); |
| | | |
| | | // 2. 合并其他单列表头(跨越两行) |
| | | // 基础信息列:A-H (0-7) |
| | | // 其他信息列:O-S (14-18) |
| | | int[] singleColumns = {0, 1, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17, 18,19}; |
| | | for (int col : singleColumns) { |
| | | sheet.addMergedRegion(new CellRangeAddress(0, 1, col, col)); |
| | | } |
| | | |
| | | // 设置响应头 |
| | | String fileName = URLEncoder.encode("员工信息导入模板.xls", "UTF-8"); |
| | | response.setContentType("application/vnd.ms-excel"); |
| | | response.setCharacterEncoding("UTF-8"); |
| | | response.setHeader("Content-Disposition", "attachment; filename=" + fileName); |
| | | response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |
| | | response.setHeader("Pragma", "no-cache"); |
| | | response.setHeader("Expires", "0"); |
| | | |
| | | // 添加CORS头 |
| | | response.setHeader("Access-Control-Allow-Origin", "*"); |
| | | response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); |
| | | response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With"); |
| | | |
| | | // 写入响应 |
| | | outputStream = response.getOutputStream(); |
| | | workbook.write(outputStream); |
| | | outputStream.flush(); |
| | | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | System.err.println("员工导入模板下载失败: " + e.getMessage()); |
| | | } finally { |
| | | try { |
| | | // 支持 YYYY-MM 格式 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM"); |
| | | return sdf.parse(dateStr); |
| | | } catch (ParseException ex) { |
| | | try { |
| | | // 支持 YYYY-MM-DD 格式 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | return sdf.parse(dateStr); |
| | | } catch (ParseException exc) { |
| | | return null; |
| | | if (outputStream != null) { |
| | | outputStream.close(); |
| | | } |
| | | if (workbook != null) { |
| | | workbook.close(); |
| | | } |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将EmployeeImportExcel转换为SysUser |
| | | * @param employeeExcel 员工导入Excel数据 |
| | |
| | | } else { |
| | | sysUser.setSex("0"); |
| | | } |
| | | |
| | | |
| | | // 身份证 |
| | | sysUser.setIdCard(employeeExcel.getIdCard()); |
| | | |
| | | |
| | | // 政治面貌 |
| | | sysUser.setPoliticalOutlook(employeeExcel.getPoliticalOutlook()); |
| | | |
| | | // 时间字段 |
| | | sysUser.setParticipationTime(parseDate(employeeExcel.getParticipationTime())); |
| | | sysUser.setCompanyTime(parseDate(employeeExcel.getCompanyTime())); |
| | | sysUser.setPositionTime(parseDate(employeeExcel.getPositionTime())); |
| | | sysUser.setParticipationTime(employeeExcel.getParticipationTime()); |
| | | sysUser.setCompanyTime(employeeExcel.getCompanyTime()); |
| | | sysUser.setPositionTime(employeeExcel.getPositionTime()); |
| | | // 学历信息 |
| | | sysUser.setFirstDegree(employeeExcel.getFirstDegree()); |
| | | sysUser.setFirstCollege(employeeExcel.getFirstCollege()); |
| | |
| | | sysUser.setHighestDegree(employeeExcel.getHighestDegree()); |
| | | sysUser.setHighestCollege(employeeExcel.getHighestCollege()); |
| | | sysUser.setHighestMajor(employeeExcel.getHighestMajor()); |
| | | |
| | | |
| | | // 职务和部门 |
| | | sysUser.setPosition(employeeExcel.getPosition()); |
| | | sysUser.setOrganization(employeeExcel.getOrganization()); |
| | | |
| | | |
| | | // 系统字段 |
| | | sysUser.setStatus("0"); // 正常状态 |
| | | sysUser.setDelFlag("0"); // 未删除 |
| | | sysUser.setCreateBy(getUsername()); |
| | | |
| | | |
| | | return sysUser; |
| | | } |
| | | |
| | | @ApiOperation(value = "员工导入模板下载") |
| | | @GetMapping("/importDownload") |
| | | public void importDownload() { |
| | | // 自定义表头,构建两行表头并合并"学历"大标题 |
| | | Workbook workbook = new HSSFWorkbook(); |
| | | Sheet sheet = workbook.createSheet("员工信息"); |
| | | |
| | | // 列宽适配 |
| | | int col = 0; |
| | | String[] firstRowHeads = new String[]{ |
| | | "序号","姓名","性别","身份证号码","年龄","政治面貌","参工时间","进入公司时间", |
| | | "学历","所在股室","职务","任职时间","电话号码","编制" |
| | | }; |
| | | String[] secondRowHeads = new String[]{ |
| | | "","","","","","","","", |
| | | "第一","学院","专业","最高","学院","专业", |
| | | "","","","","","" |
| | | }; |
| | | |
| | | // 创建样式 |
| | | CellStyle headStyle = workbook.createCellStyle(); |
| | | headStyle.setAlignment(HorizontalAlignment.CENTER); |
| | | headStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | headStyle.setBorderBottom(BorderStyle.THIN); |
| | | headStyle.setBorderTop(BorderStyle.THIN); |
| | | headStyle.setBorderLeft(BorderStyle.THIN); |
| | | headStyle.setBorderRight(BorderStyle.THIN); |
| | | Font font = workbook.createFont(); |
| | | font.setBold(true); |
| | | headStyle.setFont(font); |
| | | |
| | | Row row1 = sheet.createRow(0); |
| | | Row row2 = sheet.createRow(1); |
| | | |
| | | // 先填充第一行 |
| | | for (int i = 0; i < firstRowHeads.length; i++) { |
| | | Cell cell = row1.createCell(i); |
| | | cell.setCellValue(firstRowHeads[i]); |
| | | cell.setCellStyle(headStyle); |
| | | sheet.setColumnWidth(i, 16 * 256); |
| | | } |
| | | // 第二行 |
| | | for (int i = 0; i < secondRowHeads.length; i++) { |
| | | Cell cell = row2.createCell(i); |
| | | cell.setCellValue(secondRowHeads[i]); |
| | | cell.setCellStyle(headStyle); |
| | | } |
| | | |
| | | // 合并除"学历"外的单列表头(两行合并): 0-7 列,以及 9-13 列 |
| | | int[][] singleMerge = new int[][]{ |
| | | {0,0},{1,1},{2,2},{3,3},{4,4},{5,5},{6,6},{7,7}, |
| | | {9,9},{10,10},{11,11},{12,12},{13,13} |
| | | }; |
| | | for (int[] m : singleMerge) { |
| | | CellRangeAddress region = new CellRangeAddress(0,1,m[0],m[1]); |
| | | sheet.addMergedRegion(region); |
| | | RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet); |
| | | RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet); |
| | | RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet); |
| | | RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet); |
| | | } |
| | | |
| | | // 合并"学历"大标题区域:第8列到第13列(共6列)第一行 |
| | | CellRangeAddress xlMerge = new CellRangeAddress(0,0,8,13); |
| | | sheet.addMergedRegion(xlMerge); |
| | | RegionUtil.setBorderBottom(BorderStyle.THIN, xlMerge, sheet); |
| | | RegionUtil.setBorderTop(BorderStyle.THIN, xlMerge, sheet); |
| | | RegionUtil.setBorderLeft(BorderStyle.THIN, xlMerge, sheet); |
| | | RegionUtil.setBorderRight(BorderStyle.THIN, xlMerge, sheet); |
| | | |
| | | HttpServletResponse response = WebUtils.response(); |
| | | ServletOutputStream outputStream = null; |
| | | try { |
| | | String fileName = URLEncoder.encode("员工信息导入模板.xls", "utf-8"); |
| | | response.reset(); |
| | | response.setCharacterEncoding("utf-8"); |
| | | response.setContentType("application/vnd.ms-excel;charset=utf-8"); |
| | | // 兼容各种浏览器文件名 |
| | | response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ";filename*=utf-8''" + fileName); |
| | | response.setHeader("Pragma", "no-cache"); |
| | | response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |
| | | outputStream = response.getOutputStream(); |
| | | workbook.write(outputStream); |
| | | outputStream.flush(); |
| | | workbook.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | System.out.println("员工导入模板下载失败!"); |
| | | } finally { |
| | | if (outputStream != null) { |
| | | try { |
| | | outputStream.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | // @ApiOperation(value = "员工导入模板下载OPTIONS") |
| | | // @RequestMapping(value = "/importDownload", method = RequestMethod.OPTIONS) |
| | | // @CrossOrigin(origins = "*") |
| | | // public void importDownloadOptions(HttpServletResponse response) { |
| | | // response.setHeader("Access-Control-Allow-Origin", "*"); |
| | | // response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); |
| | | // response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With"); |
| | | // response.setStatus(HttpServletResponse.SC_OK); |
| | | // } |
| | | |
| | | /** |
| | | * 修改用户 |