From f4ddea97eae73d8a5c43226f6c5f490ed4009a3f Mon Sep 17 00:00:00 2001 From: sh Date: Thu, 29 Jan 2026 14:49:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9excel=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/cms/domain/vo/JobExcelVo.java | 7 +- .../cms/service/impl/JobServiceImpl.java | 67 ++++++++- .../java/com/ruoyi/cms/util/StringUtil.java | 58 +++++++- .../ruoyi/cms/util/excel/LongConverter.java | 140 ++++++++++++++++++ 4 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/LongConverter.java diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobExcelVo.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobExcelVo.java index b7d1b74..7bdeefe 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobExcelVo.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobExcelVo.java @@ -2,6 +2,7 @@ package com.ruoyi.cms.domain.vo; import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.cms.util.excel.LongConverter; import com.ruoyi.common.annotation.Excel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -18,12 +19,12 @@ public class JobExcelVo @ApiModelProperty("职位名称") private String jobTitle; - @ExcelProperty(value = "职位名称", index = 1) + @ExcelProperty(value = "职位名称", index = 1,converter = LongConverter.class) @Excel(name = "最小薪资", readConverterExp = "元=") @ApiModelProperty("最小薪资(元)") private Long minSalary; - @ExcelProperty(value = "职位名称", index = 2) + @ExcelProperty(value = "职位名称", index = 2,converter = LongConverter.class) @Excel(name = "最大薪资", readConverterExp = "元=") @ApiModelProperty("最大薪资(元)") private Long maxSalary; @@ -43,7 +44,7 @@ public class JobExcelVo @ApiModelProperty("用人单位名称") private String companyName; - @ExcelProperty(value = "职位名称", index = 6) + @ExcelProperty(value = "职位名称", index = 6,converter = LongConverter.class) @Excel(name = "招聘人数") @ApiModelProperty("招聘人数") private Long vacancies; diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobServiceImpl.java index 5fb00f3..87b6072 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobServiceImpl.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobServiceImpl.java @@ -1193,6 +1193,9 @@ public class JobServiceImpl extends ServiceImpl implements IJobSe BeanUtils.copyProperties(it, job); //字典转换 String education = DictUtils.getDictValue("education", it.getEducation()); + if (education == null || education.trim().isEmpty()) { + education = StringUtil.getEduCodeByDesc(it.getEducation()); + } String experience = DictUtils.getDictValue("experience", it.getExperience()); String jobType = DictUtils.getDictValue("job_type", it.getJobType()); String type = DictUtils.getDictValue("position_type", it.getType()); @@ -1259,11 +1262,65 @@ public class JobServiceImpl extends ServiceImpl implements IJobSe continue; } - JobContact contact = new JobContact(); - contact.setJobId(matchedJob.getJobId()); - contact.setContactPerson(vo.getContactPerson()); - contact.setContactPersonPhone(vo.getContactPersonPhone()); - contactList.add(contact); + List contactNames = StringUtil.splitCellMultiLineData(vo.getContactPerson()); + List contactPhones = StringUtil.splitContactPhones(vo.getContactPersonPhone()); + + if (contactNames.isEmpty() || contactPhones.isEmpty()) { + System.out.printf( + "警告:岗位【%s】的联系人姓名或手机号为空!姓名:%s,手机号:%s,跳过该组数据%n", + matchedJob.getJobTitle(), vo.getContactPerson(), vo.getContactPersonPhone() + ); + continue; + } + + int nameSize = contactNames.size(); + int phoneSize = contactPhones.size(); + int phoneIndex = 0; + + for (int nameIdx = 0; nameIdx < nameSize; nameIdx++) { + String currentName = contactNames.get(nameIdx).trim(); + if (StringUtils.isBlank(currentName)) { + continue; + } + + int phonesForCurrentName; + if (nameSize == 1) { + phonesForCurrentName = phoneSize - phoneIndex; + } else { + phonesForCurrentName = 1; + } + + // 避免索引越界,修正实际可匹配的手机号数量 + int actualPhoneCount = Math.min(phonesForCurrentName, phoneSize - phoneIndex); + if (actualPhoneCount <= 0) { + break; + } + + // 为当前姓名分配对应的手机号(1个或多个) + for (int p = 0; p < actualPhoneCount; p++) { + String currentPhone = contactPhones.get(phoneIndex).trim(); + if (StringUtils.isBlank(currentPhone)) { + phoneIndex++; + continue; + } + + // 封装联系人数据 + JobContact contact = new JobContact(); + contact.setJobId(matchedJob.getJobId()); + contact.setContactPerson(currentName); + contact.setContactPersonPhone(currentPhone); + contactList.add(contact); + + phoneIndex++; + } + } + + if (phoneIndex < phoneSize) { + System.out.printf( + "提示:岗位【%s】有 %d 个手机号未分配对应姓名,已忽略%n", + matchedJob.getJobTitle(), phoneSize - phoneIndex + ); + } } return contactList; diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java index a248420..d462400 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java @@ -3,10 +3,7 @@ package com.ruoyi.cms.util; import com.ruoyi.common.utils.StringUtils; import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; public class StringUtil { @@ -90,6 +87,30 @@ public class StringUtil { */ public static final String SFZ_VALID_REGEX="^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + public static final Map EDU_DESC_TO_CODE_MAP; + + static { + Map tempMap = new HashMap<>(); + tempMap.put("中专及以上", "1"); + tempMap.put("高中及以上", "2"); + tempMap.put("大专及以上", "3"); + tempMap.put("本科及以上", "4"); + tempMap.put("硕士及以上", "5"); + EDU_DESC_TO_CODE_MAP = Collections.unmodifiableMap(tempMap); + } + + /** + * 获取学历 + * @param eduDesc + * @return + */ + public static String getEduCodeByDesc(String eduDesc) { + if (eduDesc == null || eduDesc.trim().isEmpty()) { + return null; + } + return EDU_DESC_TO_CODE_MAP.get(eduDesc.trim()); + } + public static Boolean isEmptyOrNull(String s){ if(Objects.isNull(s)){return true;} return s.isEmpty(); @@ -229,4 +250,33 @@ public class StringUtil { } return str.toUpperCase(); } + + /** + * 工具方法:拆分 Excel 单元格内的多行数据(按换行符拆分) + */ + public static List splitCellMultiLineData(String cellValue) { + if (StringUtils.isBlank(cellValue)) { + return Collections.emptyList(); + } + // 按换行符拆分(兼容 \n 和 \r\n),并过滤空字符串、纯空格字符串 + return Arrays.stream(cellValue.split("\\r?\\n")) + .map(String::trim) + .filter(StringUtils::hasText) + .collect(Collectors.toList()); + } + + /** + * 工具方法:拆分联系人手机号(支持换行、逗号分隔,兼容1人多号) + */ + public static List splitContactPhones(String contactPhoneStr) { + if (StringUtils.isBlank(contactPhoneStr)) { + return Collections.emptyList(); + } + // 先按换行符拆分,再按逗号拆分,最后过滤空值和纯空格 + return Arrays.stream(contactPhoneStr.split("\\r?\\n")) // 处理多行 + .flatMap(line -> Arrays.stream(line.split(",|,"))) // 处理逗号(中文+英文) + .map(String::trim) + .filter(StringUtils::hasText) + .collect(Collectors.toList()); + } } diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/LongConverter.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/LongConverter.java new file mode 100644 index 0000000..8d2c858 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/LongConverter.java @@ -0,0 +1,140 @@ +package com.ruoyi.cms.util.excel; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.util.HashSet; +import java.util.Set; + +/** + * 最终版 LongConverter: + * 1. 正常数字(含单位)→ 正确Long值 + * 2. 面议/无有效数字 → 返回0 + * 3. 保留详细日志,便于排查 + */ +public class LongConverter implements Converter { + + // 需清洗的特殊字符(仅去掉单位/分隔符,保留数字) + private static final Set CLEAN_CHARS = new HashSet() {{ + add("元"); // 金额单位 + add("/"); // 分隔符 + add("月"); // 时间单位 + add(","); // 千分位 + add("k"); // 千单位 + add("K"); // 大写K + }}; + + // 需转换为0的特殊文本(可扩展,比如"无"、"面议"等) + private static final Set SPECIAL_TEXT = new HashSet() {{ + add("面议"); + add("无"); + add("面议薪资"); + add("薪资面议"); + }}; + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + /** + * 读Excel:核心逻辑(适配「面议」转0) + */ + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + // 1. 优先获取单元格的字符串值(适配所有EasyExcel版本) + String rawValue = null; + if (cellData != null) { + rawValue = cellData.getStringValue(); + // 数值型单元格兜底 + if (StringUtils.isBlank(rawValue) && cellData.getNumberValue() != null) { + rawValue = cellData.getNumberValue().toString(); + } + } + + // 2. 空值/空白字符串 → 返回0 + if (StringUtils.isBlank(rawValue)) { + return 0L; + } + String trimmedValue = rawValue.trim(); + + // 3. 匹配"面议"等特殊文本 → 直接返回0 + if (SPECIAL_TEXT.contains(trimmedValue)) { + System.out.printf("【特殊处理】单元格%s,原始值:%s → 转换为0%n", + getCellPos(cellData), trimmedValue); + return 0L; + } + + // 4. 清洗特殊字符(仅去掉单位/分隔符,保留数字) + String cleanValue = cleanSpecialChars(trimmedValue); + // 清洗后无有效数字 → 返回0 + if (StringUtils.isBlank(cleanValue)) { + System.err.printf("【无有效数字】单元格%s,原始值:%s → 转换为0%n", + getCellPos(cellData), trimmedValue); + return 0L; + } + + // 5. 处理科学计数法 + cleanValue = handleScientificNotation(cleanValue); + + // 6. 转换为Long(异常则返回0) + try { + BigDecimal bd = new BigDecimal(cleanValue); + // 非整数也转成整数(比如1999.9→1999,或返回0,根据需求选) + return bd.longValue(); // 若要严格整数,用longValueExact(),异常时走catch返回0 + } catch (NumberFormatException e) { + System.err.printf("【转换失败】单元格%s,原始值:%s,清洗后:%s,错误:%s → 转换为0%n", + getCellPos(cellData), trimmedValue, cleanValue, e.getMessage()); + return 0L; + } + } + + /** + * 写Excel:Long转字符串 + */ + @Override + public WriteCellData convertToExcelData(Long value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (value == null || value == 0L) { + return new WriteCellData<>("面议"); // 导出时0值显示为"面议",可根据需求修改 + } + return new WriteCellData<>(String.valueOf(value)); + } + + // 辅助方法:清洗特殊字符 + private String cleanSpecialChars(String value) { + String result = value; + for (String charToClean : CLEAN_CHARS) { + result = result.replace(charToClean, ""); + } + return result.trim(); + } + + // 辅助方法:处理科学计数法 + private String handleScientificNotation(String value) { + if (StringUtils.isBlank(value) || !value.matches("[+-]?\\d+(\\.\\d+)?[eE][+-]?\\d+")) { + return value; + } + return new BigDecimal(value).toPlainString(); + } + + // 辅助方法:获取单元格位置(简化日志) + private String getCellPos(ReadCellData cellData) { + if (cellData == null) { + return "未知位置"; + } + return cellData.getRowIndex() + "行" + cellData.getColumnIndex() + "列"; + } +} \ No newline at end of file