修改excel上传问题

This commit is contained in:
sh
2026-01-29 14:49:09 +08:00
parent 5af500cc0a
commit f4ddea97ea
4 changed files with 260 additions and 12 deletions

View File

@@ -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;

View File

@@ -1193,6 +1193,9 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> 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<JobMapper,Job> implements IJobSe
continue;
}
List<String> contactNames = StringUtil.splitCellMultiLineData(vo.getContactPerson());
List<String> 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(vo.getContactPerson());
contact.setContactPersonPhone(vo.getContactPersonPhone());
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;

View File

@@ -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<String, String> EDU_DESC_TO_CODE_MAP;
static {
Map<String, String> 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<String> 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<String> 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());
}
}

View File

@@ -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<Long> {
// 需清洗的特殊字符(仅去掉单位/分隔符,保留数字)
private static final Set<String> CLEAN_CHARS = new HashSet<String>() {{
add(""); // 金额单位
add("/"); // 分隔符
add(""); // 时间单位
add(","); // 千分位
add("k"); // 千单位
add("K"); // 大写K
}};
// 需转换为0的特殊文本可扩展比如"无"、"面议"等)
private static final Set<String> SPECIAL_TEXT = new HashSet<String>() {{
add("面议");
add("");
add("面议薪资");
add("薪资面议");
}};
@Override
public Class<Long> 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;
}
}
/**
* 写ExcelLong转字符串
*/
@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() + "";
}
}