diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsJobController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsJobController.java index c9d4024..0107bd6 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsJobController.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsJobController.java @@ -1,14 +1,21 @@ package com.ruoyi.cms.controller.cms; +import cn.hutool.core.bean.BeanUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.fastjson.JSON; import com.ruoyi.cms.domain.*; +import com.ruoyi.cms.domain.query.CompanySearch; import com.ruoyi.cms.domain.query.ESJobSearch; import com.ruoyi.cms.domain.query.JobSearch; import com.ruoyi.cms.domain.vo.CandidateVO; import com.ruoyi.cms.domain.vo.CompanyVo; +import com.ruoyi.cms.domain.vo.ContactParseResult; +import com.ruoyi.cms.domain.vo.JobLocalExcelVo; import com.ruoyi.cms.service.*; import com.ruoyi.cms.util.RoleUtils; import com.ruoyi.cms.util.StringUtil; +import com.ruoyi.cms.util.excel.JobExcelListener; import com.ruoyi.cms.util.sensitiveWord.SensitiveWordChecker; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.annotation.Log; @@ -26,16 +33,17 @@ import com.ruoyi.common.utils.poi.ExcelUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; /** * 岗位Controller @@ -381,5 +389,87 @@ public class CmsJobController extends BaseController return success(jobService.updateReviewStatus(jobId, reviewStatus)); } + /** + * 通用上传请求(单个) + */ + @PostMapping("/shzLocalUploadFile") + @PreAuthorize("@ss.hasPermi('cms:job:shzLocalUploadFile')") + @ApiOperation("石河子本地岗位上传") + public AjaxResult shzLocalUploadFile(@RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return AjaxResult.error("上传文件不能为空"); + } + String fileName = file.getOriginalFilename(); + if (fileName == null || !fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) { + return AjaxResult.error("请上传Excel格式的文件"); + } + try (InputStream inputStream = file.getInputStream()){ + JobExcelListener listener = new JobExcelListener(); + EasyExcel.read(inputStream, JobLocalExcelVo.class, listener).extraRead(CellExtraTypeEnum.MERGE).sheet(1).headRowNumber(2).doRead(); + + if (listener.hasColumnError()) { + return AjaxResult.error(listener.getColumnErrorMsg()); + } + List validDataList = listener.getValidDataList(); + + if (CollectionUtils.isEmpty(validDataList)) { + return AjaxResult.error("Excel文件中无有效数据!"); + } + //获取企业id + List allCompany = companyService.selectCompanyList(new CompanySearch()); + String string = UUID.randomUUID().toString(); + //获取jobs + List jobs = validDataList.stream().map(excelVo -> { + Job job = new Job(); + // 基础字段赋值 + BeanUtil.copyProperties(excelVo, job); + job.setIsPublish(1);//发布 + job.setView(0L); + job.setApplyNum(0); + job.setCreateBy(string); + job.setJobLocation(job.getJobAddress()); + job.setCompanyId(getCompanyId(allCompany, job.getCompanyName())); + job.setPostingDate(DateUtils.getTime()); + String companyName=StringUtils.isNotEmpty(job.getCompanyName())?job.getCompanyName():null; + // 联系方式转换 + List contactList = new ArrayList<>(); + ContactParseResult parseResult = excelVo.getContactParseResult(); + if (parseResult != null && parseResult.getContactItemList() != null) { + parseResult.getContactItemList().stream() + .filter(item -> StringUtils.isNotBlank(item.getPhone().trim())) + .map(item -> { + JobContact c = new JobContact(); + c.setContactPerson(companyName); + c.setContactPersonPhone(item.getPhone().trim()); + c.setRemark(item.getRemark()); + return c; + }).forEach(contactList::add); + } + if (!contactList.isEmpty()) { + job.setJobContactList(contactList); + } + return job; + }).collect(Collectors.toList()); + jobService.shzLocalUploadFile(jobs); + return AjaxResult.success("已上传!"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error(e.getMessage()); + } + } + + /** + * @param allCompany + * @param aab004 + * @return + */ + private Long getCompanyId(List allCompany, String aab004) { + for (Company company:allCompany){ + if(company.getName().equals(aab004)){ + return company.getCompanyId(); + } + } + return null; + } } diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java index 6d065d4..641fd35 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java @@ -128,7 +128,7 @@ public class Job extends BaseEntity @ApiModelProperty("是否发布 0未发布 1发布") private Integer isPublish; - @ApiModelProperty("数据来源") + @ApiModelProperty("数据来源 5本地石河子模板导入") private String dataSource; @ApiModelProperty("岗位链接") diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactParseResult.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactParseResult.java new file mode 100644 index 0000000..1857e9d --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactParseResult.java @@ -0,0 +1,14 @@ +package com.ruoyi.cms.domain.vo; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class ContactParseResult { + // 单元格原始完整文本(用于拼接进描述) + private String rawText; + // 拆分后的多条联系人 + private List contactItemList= new ArrayList<>(); +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactUploadItem.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactUploadItem.java new file mode 100644 index 0000000..6b42215 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/ContactUploadItem.java @@ -0,0 +1,13 @@ +package com.ruoyi.cms.domain.vo; + +import lombok.Data; + +@Data +public class ContactUploadItem { + // 联系人姓名:马经理、王女士、刘老师 + private String name; + // 号码:手机号/固话 + private String phone; + // 剩余备注:(微信同步)、部分岗位有五险一金 + private String remark; +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/EducationParseResult.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/EducationParseResult.java new file mode 100644 index 0000000..7db3a44 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/EducationParseResult.java @@ -0,0 +1,11 @@ +package com.ruoyi.cms.domain.vo; + +import lombok.Data; + +@Data +public class EducationParseResult { + // 学历编码 -1=不限 + private Integer educationCode; + // 原始学历文本 + private String rawText; +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobLocalExcelVo.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobLocalExcelVo.java new file mode 100644 index 0000000..bd35463 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobLocalExcelVo.java @@ -0,0 +1,127 @@ +package com.ruoyi.cms.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.cms.domain.JobContact; +import com.ruoyi.cms.util.excel.ContactParseConverter; +import com.ruoyi.cms.util.excel.EducationConverter; +import com.ruoyi.cms.util.excel.SalaryParseConverter; +import com.ruoyi.cms.util.excel.VacancyLongConverter; +import com.ruoyi.common.annotation.Excel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 岗位对象 job + */ +@Data +public class JobLocalExcelVo +{ + + @ExcelProperty(value = "序号", index = 0) + @Excel(name = "序号") + @ApiModelProperty("序号") + private String xh; + + @ExcelProperty(value = "所属行业", index = 1) + @Excel(name = "所属行业") + @ApiModelProperty("所属行业") + private String jobCategory; + + @ExcelProperty(value = "单位名称", index = 2) + @Excel(name = "单位名称") + @ApiModelProperty("单位名称") + private String companyName; + + @ExcelProperty(value = "工作地点", index = 3) + @ApiModelProperty("工作地点") + private String jobAddress; + + @ExcelProperty(value = "岗位工种名称", index = 4) + @Excel(name = "岗位工种名称") + @ApiModelProperty("岗位工种名称") + private String jobTitle; + + @ExcelProperty(value = "招聘人数", index = 5,converter = VacancyLongConverter.class) + @Excel(name = "招聘人数") + @ApiModelProperty("招聘人数") + private Long vacancies; + + @ExcelProperty(value = "任职要求", index = 6) + @ApiModelProperty("任职要求") + private String description; + + @ExcelProperty(value = "学历要求", index = 7,converter = EducationConverter.class) + private EducationParseResult educationResult; + + @ExcelProperty(value = "工资待遇元/月", index = 8, converter = SalaryParseConverter.class) + @Excel(name = "工资待遇元/月") + @ApiModelProperty("工资待遇元/月") + private SalaryParseDTO salaryParseDto; + + @ExcelProperty(value = "联系人对象", index = 9, converter = ContactParseConverter.class) + private ContactParseResult contactParseResult; + + @ExcelProperty(value = "岗位收集人", index = 10) + @ApiModelProperty("岗位收集人") + private String gwsjr; + + @ExcelProperty(value = "联系人") + @ApiModelProperty("联系人") + private String contactPerson; + + @ExcelProperty(value = "联系人电话") + @ApiModelProperty("联系人电话") + private String contactPersonPhone; + + @ExcelProperty(value = "最小薪资") + @Excel(name = "最小薪资", readConverterExp = "元=") + @ApiModelProperty("最小薪资(元)") + private Long minSalary; + + @ExcelProperty(value = "最大薪资") + @Excel(name = "最大薪资", readConverterExp = "元=") + @ApiModelProperty("最大薪资(元)") + private Long maxSalary; + + @ExcelProperty(value = "岗位区划") + @Excel(name = "岗位区划") + @ApiModelProperty("岗位区划") + private String jobLocation; + + @ExcelProperty(value = "工作经验") + @Excel(name = "工作经验要求 对应字典experience") + @ApiModelProperty("工作经验要求 对应字典experience") + private String experience; + + @ExcelProperty(value = "岗位类型") + @ApiModelProperty("岗位类型 0疆内 1疆外") + private String jobType; + + @ExcelProperty(value = "类型") + @ApiModelProperty("类型 0常规岗位 1就业见习岗位 2实习实训岗位 3社区实践岗位 4零工 对应字段字典position_type") + private String type; + + @ApiModelProperty("是否发布 0未发布 1发布") + private Integer isPublish; + + @ApiModelProperty("工作地点区县字典代码") + private Integer jobLocationAreaCode; + + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd") + @ApiModelProperty("发布时间") + private String postingDate; + + @ApiModelProperty("数据来源") + private String dataSource; + + @Excel(name = "学历要求 对应字典education") + @ApiModelProperty("学历要求 对应字典education") + private String education; + + @ApiModelProperty("联系人列表") + private List jobContacts; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/SalaryParseDTO.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/SalaryParseDTO.java new file mode 100644 index 0000000..b876260 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/SalaryParseDTO.java @@ -0,0 +1,19 @@ +package com.ruoyi.cms.domain.vo; + +import com.ruoyi.cms.util.excel.SalaryType; +import lombok.Data; +import java.math.BigDecimal; + +@Data +public class SalaryParseDTO { + // 薪资类型 + private SalaryType salaryType; + // 最低薪资(按月统一换算,单位:元) + private Long minMoney; + // 最高薪资 + private Long maxMoney; + // 原始文本 + private String originalText; + // 福利/备注剩余文字 + private String remark; +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/IJobService.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/IJobService.java index 6336620..e155036 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/IJobService.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/IJobService.java @@ -116,4 +116,6 @@ public interface IJobService Long updateReviewStatus(Long jobId, String reviewStatus); List getRecommendByJobId(Long jobId); + + void shzLocalUploadFile(List jobs); } 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 0e79be2..d5122b5 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 @@ -1286,4 +1286,23 @@ public class JobServiceImpl extends ServiceImpl implements IJobSe } return list; } + + @Override + public void shzLocalUploadFile(List jobs) { + List allContactList = new ArrayList<>(); + for (Job job : jobs) { + jobMapper.insert(job); + List contactList = job.getJobContactList(); + if (CollectionUtils.isEmpty(contactList)) { + continue; + } + contactList.forEach(c -> c.setJobId(job.getJobId())); + allContactList.addAll(contactList); + } + if (!allContactList.isEmpty()) { + jobContactMapper.batchInsert(allContactList); + } + //刷新es + iesJobSearchService.resetTextCache(); + } } 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 8b75287..d5a7e97 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 @@ -11,6 +11,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class StringUtil { @@ -97,6 +99,16 @@ public class StringUtil { private static final String SEPARATOR = "-"; private static final String DEFAULT_VALUE = "0"; + /** + * 本地石河子模板导入 + */ + public static final String DATA_SOURCE_LOCAL = "5"; + + /** + * 截取数字 + */ + private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d+"); + /** * 身份证规则 */ @@ -349,4 +361,31 @@ public class StringUtil { // 两端不同,正常拼接 return min + SEPARATOR + max; } + + /** + * 处理上传中人数问题 + * @param source + * @return + */ + public static Long extractVacancyNum(String source) { + if (StringUtils.isBlank(source)) { + return 1L; + } + String text = source.trim(); + if (text.contains("若干")) { + return 20L; + } + Matcher matcher = DIGIT_PATTERN.matcher(text); + Long maxNum = null; + while (matcher.find()) { + String numStr = matcher.group(); + try { + long num = Long.parseLong(numStr); + if (maxNum == null || num > maxNum) { + maxNum = num; + } + } catch (NumberFormatException ignored) {} + } + return maxNum == null ? 1L : maxNum; + } } diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseConverter.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseConverter.java new file mode 100644 index 0000000..9b3e97c --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseConverter.java @@ -0,0 +1,48 @@ +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.property.ExcelContentProperty; +import com.ruoyi.cms.domain.vo.ContactParseResult; + +import java.math.BigDecimal; + +public class ContactParseConverter implements Converter { + + @Override + public ContactParseResult convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + ContactParseResult res = new ContactParseResult(); + String raw = ""; + if (cellData != null) { + // 正确枚举:CellDataTypeEnum,包路径enums下 + CellDataTypeEnum cellType = cellData.getType(); + if (CellDataTypeEnum.STRING.equals(cellType)) { + raw = cellData.getStringValue() == null ? "" : cellData.getStringValue(); + } else if (CellDataTypeEnum.NUMBER.equals(cellType)) { + // 数字单元格转完整字符串,避免科学计数 + BigDecimal numVal = cellData.getNumberValue(); + raw = numVal.toPlainString(); + } else if (CellDataTypeEnum.BOOLEAN.equals(cellType)) { + raw = String.valueOf(cellData.getBooleanValue()); + } else if (CellDataTypeEnum.EMPTY.equals(cellType)) { + raw = ""; + } + } + res.setRawText(raw); + // 拆分多条联系人 + res.setContactItemList(ContactParseUtil.parse(raw)); + return res; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Class supportJavaTypeKey() { + return ContactParseResult.class; + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseUtil.java new file mode 100644 index 0000000..ff8036c --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/ContactParseUtil.java @@ -0,0 +1,227 @@ +package com.ruoyi.cms.util.excel; + +import com.ruoyi.cms.domain.vo.ContactUploadItem; +import org.apache.commons.lang3.StringUtils; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 分层联系人解析工具 + * 方案B:同时支持手机号、座机、邮箱,解决1750993xxxx 带出0993xxxx问题 + * 实现逻辑:先提取并屏蔽手机号,再解析座机,避免手机号内部数字误匹配座机 + * name固定空,remark存储Excel完整原始单元格文本 + * \n /
自动拆分多行 + */ +public class ContactParseUtil { + + // 11位纯数字手机号 + private static final Pattern MOBILE_11 = Pattern.compile("1[3-9]\\d{9}"); + // 11位掩码手机号 + private static final Pattern MOBILE_MASK_11 = Pattern.compile("1[3-9][0-9xX]{10}"); + // 带横杠固定电话 0993-xxxxxx / 0993—xxxxxx + private static final Pattern TEL_FULL = Pattern.compile("(0\\d{2,4}[—-])(\\d{4,8})"); + // 无横杠纯数字座机 0开头6~12位 + private static final Pattern TEL_PURE_NUM = Pattern.compile("0\\d{6,12}"); + // 提取区号前缀 + private static final Pattern AREA_PREFIX = Pattern.compile("0\\d{2,4}[—-]"); + // 兼容掩码邮箱正则 + private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9xX._-]+@[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_.+-]+"); + + // 对外统一入口 + public static List parse(String source) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(source)) { + return result; + } + String fullOriginalText = source; + List lineList = splitTopMultiLine(source); + for (String line : lineList) { + String trimLine = line.trim(); + if (StringUtils.isBlank(trimLine)) { + continue; + } + if (trimLine.contains(":")) { + handleColonTypeLine(trimLine, result, fullOriginalText); + } else { + handleNormalLine(trimLine, result, fullOriginalText); + } + } + return result; + } + + /** + * \n \r

全部作为独立分隔符,拆分为多行联系人 + */ + private static List splitTopMultiLine(String source) { + List lineResult = new ArrayList<>(); + String temp = source.replace("\n", "|") + .replace("\r", "|") + .replace("
", "|") + .replace("
", "|"); + String[] arr = temp.split("\\|"); + for (String s : arr) { + String trimStr = s.trim(); + if (StringUtils.isNotBlank(trimStr)) { + lineResult.add(trimStr); + } + } + return lineResult; + } + + /** + * 处理 文字:联系方式 冒号格式,name固定空 + */ + private static void handleColonTypeLine(String line, List result, String fullSource) { + String[] colonArr = line.split(":", 2); + String contactArea = colonArr.length > 1 ? colonArr[1].trim() : ""; + String contactVal = extractAllContact(contactArea); + if (StringUtils.isBlank(contactVal)) { + return; + } + ContactUploadItem item = new ContactUploadItem(); + item.setName(""); + item.setPhone(contactVal); + item.setRemark(fullSource); + result.add(item); + } + + /** + * 无冒号普通行,不提取任何汉字姓名,name固定空 + */ + private static void handleNormalLine(String line, List result, String fullSource) { + boolean hasContact = existContact(line); + if (!hasContact) { + return; + } + String areaPrefix = extractAreaPrefix(line); + List segList = splitContactSegment(line); + for (String seg : segList) { + String segTrim = seg.trim(); + if (StringUtils.isBlank(segTrim)) { + continue; + } + // 判断当前片段是否是完整手机号,是则不拼接区号 + boolean isMobile = checkTextHasMobile(segTrim); + String segComplete; + if (isMobile) { + segComplete = segTrim; + } else { + segComplete = autoFillAreaCode(segTrim, areaPrefix); + } + String contactVal = extractAllContact(segComplete); + if (StringUtils.isBlank(contactVal)) { + continue; + } + ContactUploadItem item = new ContactUploadItem(); + item.setName(""); + item.setPhone(contactVal); + item.setRemark(fullSource); + result.add(item); + } + } + + /** + * 判断文本是否包含手机号(纯数字/掩码) + */ + private static boolean checkTextHasMobile(String text) { + return MOBILE_MASK_11.matcher(text).find() + || MOBILE_11.matcher(text).find(); + } + + /** + * 判断文本是否包含任意联系方式 + */ + private static boolean existContact(String text) { + return MOBILE_MASK_11.matcher(text).find() + || MOBILE_11.matcher(text).find() + || TEL_FULL.matcher(text).find() + || TEL_PURE_NUM.matcher(text).find() + || EMAIL_PATTERN.matcher(text).find(); + } + + /** + * 提取全部有效联系方式:手机号、带横杠固话、纯数字座机、邮箱 + * 核心修复逻辑:先缓存手机号、屏蔽手机号文本,再匹配座机,避免手机号内部数字误匹配座机 + */ + private static String extractAllContact(String text) { + List mobileCache = new ArrayList<>(); + // 1. 缓存所有手机号,并用占位符替换,屏蔽内部数字 + Matcher maskMobileMatcher = MOBILE_MASK_11.matcher(text); + while (maskMobileMatcher.find()) { + mobileCache.add(maskMobileMatcher.group()); + } + String tempText = text.replaceAll(MOBILE_MASK_11.pattern(), "#MOBILE#"); + + Matcher mobileMatcher = MOBILE_11.matcher(tempText); + while (mobileMatcher.find()) { + mobileCache.add(mobileMatcher.group()); + } + tempText = tempText.replaceAll(MOBILE_11.pattern(), "#MOBILE#"); + + StringBuilder sb = new StringBuilder(); + // 2. 先回填所有手机号 + for (String mobile : mobileCache) { + sb.append(mobile).append(" "); + } + + // 3. 在屏蔽手机号后的文本里匹配座机,不会捕获手机号内部数字 + // 带横杠固话 + Matcher telMatcher = TEL_FULL.matcher(tempText); + while (telMatcher.find()) { + sb.append(telMatcher.group(1)).append(telMatcher.group(2)).append(" "); + } + // 无横杠纯数字座机 + Matcher telPureMatcher = TEL_PURE_NUM.matcher(tempText); + while (telPureMatcher.find()) { + sb.append(telPureMatcher.group()).append(" "); + } + // 4. 匹配邮箱不受影响 + Matcher emailMatcher = EMAIL_PATTERN.matcher(text); + while (emailMatcher.find()) { + sb.append(emailMatcher.group()).append(" "); + } + return sb.toString().trim(); + } + + /** + * 提取区号前缀 + */ + private static String extractAreaPrefix(String text) { + Matcher matcher = AREA_PREFIX.matcher(text); + if (matcher.find()) { + return matcher.group(); + } + return ""; + } + + /** + * 仅4~8位纯数字短号 且 不是手机号才自动拼接区号 + */ + private static String autoFillAreaCode(String seg, String areaCode) { + if (StringUtils.isBlank(areaCode)) { + return seg; + } + // 只有纯数字4-8位,且不包含手机号才补区号 + if (seg.matches("\\d{4,8}") && !checkTextHasMobile(seg)) { + return areaCode + seg; + } + return seg; + } + + /** + * 按 / , ,分割多个联系方式片段 + */ + private static List splitContactSegment(String line) { + List segResult = new ArrayList<>(); + String[] arr = line.split("[/,,]"); + for (String s : arr) { + String trimStr = s.trim(); + if (StringUtils.isNotBlank(trimStr)) { + segResult.add(trimStr); + } + } + return segResult; + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationConverter.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationConverter.java new file mode 100644 index 0000000..9421a62 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationConverter.java @@ -0,0 +1,40 @@ +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.property.ExcelContentProperty; +import com.ruoyi.cms.domain.vo.EducationParseResult; + +/** + * 学历文本转字典编码转换器 + */ +public class EducationConverter implements Converter { + + @Override + public EducationParseResult convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + String text = ""; + if (cellData != null && cellData.getStringValue() != null) { + text = cellData.getStringValue(); + } + try { + return EducationParseUtil.parseDetail(text); + } catch (Exception e) { + EducationParseResult err = new EducationParseResult(); + err.setRawText(text); + err.setEducationCode(-1); + return err; + } + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationParseUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationParseUtil.java new file mode 100644 index 0000000..cb3856d --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/EducationParseUtil.java @@ -0,0 +1,93 @@ +package com.ruoyi.cms.util.excel; + +import com.ruoyi.cms.domain.vo.EducationParseResult; +import org.apache.commons.lang3.StringUtils; +import java.util.HashMap; +import java.util.Map; + +public class EducationParseUtil { + private static final Map EDUCATION_MAP = new HashMap<>(); + + static { + EDUCATION_MAP.put("初中及以下", 0); + EDUCATION_MAP.put("中专/中技", 1); + EDUCATION_MAP.put("高中", 2); + EDUCATION_MAP.put("大专", 3); + EDUCATION_MAP.put("本科", 4); + EDUCATION_MAP.put("硕士", 5); + EDUCATION_MAP.put("博士", 6); + EDUCATION_MAP.put("mba/emba", 7); + EDUCATION_MAP.put("留学-学士", 8); + EDUCATION_MAP.put("留学-硕士", 9); + EDUCATION_MAP.put("留学-博士", 10); + EDUCATION_MAP.put("不限", -1); + } + + /** + * 只返回编码(原有方法保留兼容) + */ + public static Integer parse(String source) { + EducationParseResult result = parseDetail(source); + return result.getEducationCode(); + } + + /** + * 完整解析,返回编码+原文+识别标记(核心) + */ + public static EducationParseResult parseDetail(String source) { + EducationParseResult result = new EducationParseResult(); + String raw = source == null ? "" : source.trim(); + result.setRawText(raw); + result.setEducationCode(-1); + + if (StringUtils.isBlank(raw)) { + // 空白:不限,识别成功 + return result; + } + String text = raw.toLowerCase(); + + // 1. 不限 + if (text.contains("不限") || text.contains("无要求") || text.equals("无")) { + result.setEducationCode(-1); + return result; + } + + // 2. MBA/EMBA + if (text.contains("mba") || text.contains("emba")) { + result.setEducationCode(7); + return result; + } + + // 3. 留学 + if (text.contains("留学") || text.contains("海外") || text.contains("国外")) { + if (text.contains("学士") || text.contains("本科")) { + result.setEducationCode(8); + return result; + } else if (text.contains("硕士")) { + result.setEducationCode(9); + return result; + } else if (text.contains("博士")) { + result.setEducationCode(10); + return result; + } + } + + // 4. 标准学历短句(正常识别) + if (text.contains("初中")) { + result.setEducationCode(0); + } else if (text.contains("中专") || text.contains("中技")) { + result.setEducationCode(1); + } else if (text.contains("高中")) { + result.setEducationCode(2); + } else if (text.contains("大专") || text.contains("专科")) { + result.setEducationCode(3); + } else if (text.contains("本科") || text.contains("大学")) { + result.setEducationCode(4); + } else if (text.contains("硕士") || text.contains("研究生")) { + result.setEducationCode(5); + } else if (text.contains("博士")) { + result.setEducationCode(6); + } + return result; + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobExcelListener.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobExcelListener.java new file mode 100644 index 0000000..0d84c3e --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobExcelListener.java @@ -0,0 +1,186 @@ +package com.ruoyi.cms.util.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.ruoyi.cms.domain.JobContact; +import com.ruoyi.cms.domain.vo.*; +import com.ruoyi.cms.util.StringUtil; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.lang.reflect.Field; + + +/** + * 企业岗位excel监听 + */ +@Getter +public class JobExcelListener extends AnalysisEventListener { + + private final List validDataList = new ArrayList<>(); + private final List mergeCellList = new ArrayList<>(); + private final Map columnFieldMap = new HashMap<>(); + private String columnErrorMsg; + + public JobExcelListener() { + initColumnFieldMap(); + } + + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + + } + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + if (extra.getType() == CellExtraTypeEnum.MERGE) { + mergeCellList.add(extra); + } + } + + @Override + public void invoke(JobLocalExcelVo jobExcelVo, AnalysisContext analysisContext) { + //薪资要求 + SalaryParseDTO dto = jobExcelVo.getSalaryParseDto(); + if (dto == null) { + dto = new SalaryParseDTO(); + dto.setSalaryType(SalaryType.FACE_TO_FACE); + dto.setOriginalText(""); + dto.setRemark("未读取到薪资列数据"); + } + jobExcelVo.setMinSalary(dto.getMinMoney()); + jobExcelVo.setMaxSalary(dto.getMaxMoney()); + String rawSalary = dto.getOriginalText(); + if (StringUtils.isNotBlank(rawSalary)) { + String desc = jobExcelVo.getDescription() == null ? "" : jobExcelVo.getDescription(); + String tag = "薪资待遇:" + rawSalary; + if (!desc.contains(tag)) { + jobExcelVo.setDescription(desc + "\n" + tag); + } + } + + ////学历要求 + EducationParseResult eduResult = jobExcelVo.getEducationResult(); + if (eduResult == null) { + eduResult = new EducationParseResult(); + eduResult.setEducationCode(-1); + eduResult.setRawText(""); + } + jobExcelVo.setEducation(String.valueOf(eduResult.getEducationCode())); + String rawEduText = eduResult.getRawText(); + if (StringUtils.isNotBlank(rawEduText)) { + String desc = jobExcelVo.getDescription() == null ? "" : jobExcelVo.getDescription(); + String tag = "学历要求:" + rawEduText; + if (!desc.contains(tag)) { + jobExcelVo.setDescription(desc + "\n" + tag); + } + } + + //处理联系方式 + ContactParseResult contactResult = jobExcelVo.getContactParseResult(); + if (contactResult == null) { + contactResult = new ContactParseResult(); + contactResult.setRawText(""); + } + // 遍历拆分好的【联系人+号码】,可单独入库 + List jobContacts=new ArrayList<>(); + for (ContactUploadItem item : contactResult.getContactItemList()) { + JobContact jobContact=new JobContact(); + if(StringUtils.isNotBlank(item.getPhone().trim())){ + String name = item.getName(); + String phone = item.getPhone(); + String remark = item.getRemark(); + jobContact.setContactPerson(name); + jobContact.setContactPersonPhone(phone); + jobContact.setRemark(remark); + jobContacts.add(jobContact); + } + } + if(!jobContacts.isEmpty()){ + jobExcelVo.setJobContacts(jobContacts); + } + //数据来源 + jobExcelVo.setDataSource(StringUtil.DATA_SOURCE_LOCAL); + validDataList.add(jobExcelVo); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + fillMergedCellData(analysisContext); + } + + /** + * 填充合并单元格空值,实现拆分效果 + */ + private void fillMergedCellData(AnalysisContext context) { + int headRowNum = context.readSheetHolder().getHeadRowNumber(); + if (columnFieldMap.isEmpty()) { + return; + } + + for (CellExtra merge : mergeCellList) { + int firstRow = merge.getFirstRowIndex(); + int lastRow = merge.getLastRowIndex(); + int firstCol = merge.getFirstColumnIndex(); + + // 跳过表头 + if (firstRow < headRowNum) { + continue; + } + Field field = columnFieldMap.get(firstCol); + if (field == null) { + continue; + } + + try { + int firstDataIndex = firstRow - headRowNum; + if (firstDataIndex >= validDataList.size()) { + continue; + } + // 获取合并首行的值 + Object mergeValue = field.get(validDataList.get(firstDataIndex)); + + // 给合并区域所有行赋值 + for (int row = firstRow + 1; row <= lastRow; row++) { + int dataIndex = row - headRowNum; + if (dataIndex >= validDataList.size()) { + continue; + } + field.set(validDataList.get(dataIndex), mergeValue); + } + } catch (Exception e) { + columnErrorMsg = "合并单元格填充失败:" + e.getMessage(); + } + } + } + + /** + * 扫描VO实体 @ExcelProperty(index) 构建列-字段映射 + */ + private void initColumnFieldMap() { + Class clazz = JobLocalExcelVo.class; + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + ExcelProperty excelProp = field.getAnnotation(ExcelProperty.class); + if (excelProp == null || excelProp.index() < 0) { + continue; + } + field.setAccessible(true); + columnFieldMap.put(excelProp.index(), field); + } + } + + + public boolean hasColumnError() { + return StringUtils.isNotBlank(columnErrorMsg); + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseConverter.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseConverter.java new file mode 100644 index 0000000..9f209c3 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseConverter.java @@ -0,0 +1,40 @@ +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.property.ExcelContentProperty; +import com.ruoyi.cms.domain.vo.SalaryParseDTO; + +public class SalaryParseConverter implements Converter { + + @Override + public SalaryParseDTO convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + String text = ""; + if (cellData != null && cellData.getStringValue() != null) { + text = cellData.getStringValue(); + } + try { + return SalaryParseUtil.parse(text); + } catch (Exception e) { + SalaryParseDTO dto = new SalaryParseDTO(); + dto.setSalaryType(SalaryType.FACE_TO_FACE); + dto.setOriginalText(""); + dto.setRemark("薪资单元格为空/解析失败"); + dto.setMinMoney(null); + dto.setMaxMoney(null); + return dto; + } + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Class supportJavaTypeKey() { + return SalaryParseDTO.class; + } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseUtil.java new file mode 100644 index 0000000..ddc9889 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryParseUtil.java @@ -0,0 +1,159 @@ +package com.ruoyi.cms.util.excel; + +import com.ruoyi.cms.domain.vo.SalaryParseDTO; +import org.apache.commons.lang3.StringUtils; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SalaryParseUtil { + + // 匹配面议关键词 + private static final Pattern FACE_PATTERN = Pattern.compile("(面议|薪资面议|工资面议)"); + // 年薪匹配 年薪14万、年薪12万元 + private static final Pattern YEAR_PATTERN = Pattern.compile("年薪(\\d+(\\.\\d+)?)[万w]"); + // 日薪 200-300/天、300元/天、200/天 + private static final Pattern DAY_PATTERN = Pattern.compile("(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?)[/元]*天|(\\d+(\\.\\d+)?)[/元]*天"); + // 时薪 100元/小时、50/小时 + private static final Pattern HOUR_PATTERN = Pattern.compile("(\\d+(\\.\\d+)?)[/元]*小时"); + // 月薪区间 3000-5000、3000--5000、3500-6000元/月 + private static final Pattern MONTH_RANGE_PATTERN = Pattern.compile("(\\d+)[-–—]{1,2}(\\d+)"); + // 固定月薪 4000元、到手3100、应发6800、3800 + private static final Pattern MONTH_FIX_PATTERN = Pattern.compile("(到手|应发)?(\\d+)"); + // 模糊薪资 3500左右、5000+、3000以上 + private static final Pattern FUZZY_PATTERN = Pattern.compile("(\\d+)(左右|\\+|以上)"); + + // 每月折算工作日22天,每日8小时 + private static final BigDecimal DAY_TO_MONTH = new BigDecimal("22"); + private static final BigDecimal HOUR_TO_DAY = new BigDecimal("8"); + + public static SalaryParseDTO parse(String text) { + SalaryParseDTO dto = new SalaryParseDTO(); + if (StringUtils.isBlank(text)) { + dto.setSalaryType(SalaryType.FACE_TO_FACE); + dto.setOriginalText(""); + return dto; + } + // 清洗文本:去除引号、换行、多余空格、单位干扰词 + String origin = text.trim(); + String cleanText = origin.replace("\"", "") + .replace("\n", "") + .replace("\r", "") + .replaceAll("\\s+", " ") + .replace("元/月", "") + .replace("元", "") + .replace("/月", ""); + dto.setOriginalText(origin); + + // 1. 判断是否面议 + if (FACE_PATTERN.matcher(cleanText).find()) { + dto.setSalaryType(SalaryType.FACE_TO_FACE); + dto.setRemark(cleanText); + return dto; + } + + // 2. 年薪解析 + Matcher yearMatcher = YEAR_PATTERN.matcher(cleanText); + if (yearMatcher.find()) { + dto.setSalaryType(SalaryType.YEAR_SALARY); + BigDecimal yearWan = new BigDecimal(yearMatcher.group(1)); + BigDecimal yearTotal = yearWan.multiply(new BigDecimal("10000")); + BigDecimal month = yearTotal.divide(new BigDecimal("12"), 2, RoundingMode.HALF_UP); + dto.setMinMoney(month.longValue()); + dto.setMaxMoney(month.longValue()); + dto.setRemark(cleanText.replace(yearMatcher.group(), "").trim()); + return dto; + } + + // 3. 日薪解析,统一折算月薪 + Matcher dayMatcher = DAY_PATTERN.matcher(cleanText); + if (dayMatcher.find()) { + dto.setSalaryType(SalaryType.DAY_SALARY); + String minDayStr = dayMatcher.group(1); + String maxDayStr = dayMatcher.group(3); + BigDecimal minDay = new BigDecimal(StringUtils.isNotBlank(minDayStr) ? minDayStr : dayMatcher.group(5)); + BigDecimal maxDay = StringUtils.isNotBlank(maxDayStr) ? new BigDecimal(maxDayStr) : minDay; + // 折算月 + BigDecimal minMonth = minDay.multiply(DAY_TO_MONTH); + BigDecimal maxMonth = maxDay.multiply(DAY_TO_MONTH); + dto.setMinMoney(minMonth.longValue()); + dto.setMaxMoney(maxMonth.longValue()); + dto.setRemark(cleanText.replace(dayMatcher.group(), "").trim()); + return dto; + } + + // 4. 时薪解析 + Matcher hourMatcher = HOUR_PATTERN.matcher(cleanText); + if (hourMatcher.find()) { + dto.setSalaryType(SalaryType.HOUR_SALARY); + BigDecimal hour = new BigDecimal(hourMatcher.group(1)); + BigDecimal day = hour.multiply(HOUR_TO_DAY); + BigDecimal month = day.multiply(DAY_TO_MONTH); + dto.setMinMoney(month.longValue()); + dto.setMaxMoney(month.longValue()); + dto.setRemark(cleanText.replace(hourMatcher.group(), "").trim()); + return dto; + } + + // 5. 模糊薪资 3500左右、5000+ + Matcher fuzzyMatcher = FUZZY_PATTERN.matcher(cleanText); + if (fuzzyMatcher.find()) { + dto.setSalaryType(SalaryType.FUZZY_MONTH); + BigDecimal num = new BigDecimal(fuzzyMatcher.group(1)); + dto.setMinMoney(num.longValue()); + dto.setMaxMoney(null); + dto.setRemark(cleanText.replace(fuzzyMatcher.group(), "").trim()); + return dto; + } + + // 6. 月薪区间 3000-5000 + Matcher rangeMatcher = MONTH_RANGE_PATTERN.matcher(cleanText); + if (rangeMatcher.find()) { + dto.setSalaryType(SalaryType.MONTH_RANGE); + BigDecimal min = new BigDecimal(rangeMatcher.group(1)); + BigDecimal max = new BigDecimal(rangeMatcher.group(2)); + dto.setMinMoney(min.longValue()); + dto.setMaxMoney(max.longValue()); + dto.setRemark(cleanText.replace(rangeMatcher.group(), "").trim()); + return dto; + } + + // 7. 固定月薪 + Matcher fixMatcher = MONTH_FIX_PATTERN.matcher(cleanText); + if (fixMatcher.find()) { + dto.setSalaryType(SalaryType.MONTH_FIX); + BigDecimal val = new BigDecimal(fixMatcher.group(2)); + dto.setMinMoney(val.longValue()); + dto.setMaxMoney(val.longValue()); + dto.setRemark(cleanText.replace(fixMatcher.group(), "").trim()); + return dto; + } + + // 兜底:无法识别归为面议 + dto.setSalaryType(SalaryType.FACE_TO_FACE); + dto.setRemark(cleanText); + return dto; + } + + // 测试方法 +// public static void main(String[] args) { +// String test1 = "5000-6000元\n五险"; +// String test2 = "工资面议,包吃包住"; +// String test3 = "200-300/天"; +// String test4 = "年薪14万元,包食宿"; +// String test5 = "3500左右"; +// String test6 = "到手3100"; +// String test7 = "100元/小时"; +// String test8 = "底薪3000+提成"; +// +// System.out.println(parse(test1)); +// System.out.println(parse(test2)); +// System.out.println(parse(test3)); +// System.out.println(parse(test4)); +// System.out.println(parse(test5)); +// System.out.println(parse(test6)); +// System.out.println(parse(test7)); +// System.out.println(parse(test8)); +// } +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryType.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryType.java new file mode 100644 index 0000000..3b4a3ca --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/SalaryType.java @@ -0,0 +1,11 @@ +package com.ruoyi.cms.util.excel; + +public enum SalaryType { + FACE_TO_FACE, // 面议 + MONTH_RANGE, // 月薪区间 + MONTH_FIX, // 固定月薪 + DAY_SALARY, // 日薪 + HOUR_SALARY, // 时薪 + YEAR_SALARY, // 年薪 + FUZZY_MONTH // 模糊(3500左右、5000+) +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/VacancyLongConverter.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/VacancyLongConverter.java new file mode 100644 index 0000000..b1acceb --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/VacancyLongConverter.java @@ -0,0 +1,63 @@ +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.property.ExcelContentProperty; +import org.apache.commons.lang3.StringUtils; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 招聘人数文本转Long转换器 + */ +public class VacancyLongConverter implements Converter { + + private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d+"); + + /** + * 读取Excel时,文本转Long + */ + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + String source = cellData.getStringValue(); + return extractVacancyNum(source); + } + + /** + * 你的原有提取逻辑完整放入转换器内部 + */ + private static Long extractVacancyNum(String source) { + if (StringUtils.isBlank(source)) { + return 1L; + } + String text = source.trim(); + if (text.contains("若干")) { + return 20L; + } + Matcher matcher = DIGIT_PATTERN.matcher(text); + Long maxNum = null; + while (matcher.find()) { + String numStr = matcher.group(); + try { + long num = Long.parseLong(numStr); + if (maxNum == null || num > maxNum) { + maxNum = num; + } + } catch (NumberFormatException ignored) { + } + } + return maxNum == null ? 1L : maxNum; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } +} \ No newline at end of file