From ffaf47bd3d4ad9ff2c154b8a6d04d52f47ccb487 Mon Sep 17 00:00:00 2001 From: chenshaohua <635616957@qq.com> Date: Thu, 30 Jul 2026 16:17:01 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=B3=E6=B2=B3=E5=AD=90=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E5=B2=97=E4=BD=8D=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cms/JobExcelImportRecordController.java | 57 ++++ .../cms/domain/JobExcelImportRecord.java | 51 +++ .../cms/domain/vo/JobShzLocalExcelVo.java | 146 +++++++++ .../mapper/JobExcelImportRecordMapper.java | 19 ++ .../service/JobExcelImportRecordService.java | 22 ++ .../impl/JobExcelImportRecordServiceImpl.java | 292 ++++++++++++++++++ .../util/excel/JobShzLocalExcelListener.java | 161 ++++++++++ .../ruoyi/cms/util/excel/UpExcelDictUtil.java | 136 ++++++++ .../mapper/app/JobExcelImportRecordMapper.xml | 57 ++++ 9 files changed, 941 insertions(+) create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/JobExcelImportRecordController.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/JobExcelImportRecord.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobShzLocalExcelVo.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobExcelImportRecordMapper.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/JobExcelImportRecordService.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobExcelImportRecordServiceImpl.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobShzLocalExcelListener.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/UpExcelDictUtil.java create mode 100644 ruoyi-bussiness/src/main/resources/mapper/app/JobExcelImportRecordMapper.xml diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/JobExcelImportRecordController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/JobExcelImportRecordController.java new file mode 100644 index 0000000..2144aef --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/JobExcelImportRecordController.java @@ -0,0 +1,57 @@ +package com.ruoyi.cms.controller.cms; + +import com.ruoyi.cms.domain.JobExcelImportRecord; +import com.ruoyi.cms.service.JobExcelImportRecordService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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 java.util.List; + +/** + * 石河子岗位数据入库监测记录Controller + */ +@RestController +@RequestMapping("/cms/jobImportRecord") +@Api(value = "后台:石河子岗位数据入库监测记录") +public class JobExcelImportRecordController extends BaseController { + + @Autowired + private JobExcelImportRecordService jobExcelImportRecordService; + + /** + * 查询列表 + */ + @PreAuthorize("@ss.hasPermi('cms:jobImportRecord:list')") + @GetMapping("/list") + public TableDataInfo list(JobExcelImportRecord shzJobExcelImportRecord) { + startPage(); + List list = jobExcelImportRecordService.selectShzJobExcelImportRecordList(shzJobExcelImportRecord); + return getDataTable(list); + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/shzLocalUploadFile") + @PreAuthorize("@ss.hasPermi('cms:jobImportRecord:upload')") + @ApiOperation("石河子本地岗位上传") + public AjaxResult upload(@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格式的文件"); + } + jobExcelImportRecordService.uploadFile(file); + return success(); + } + +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/JobExcelImportRecord.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/JobExcelImportRecord.java new file mode 100644 index 0000000..b3dfcaa --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/JobExcelImportRecord.java @@ -0,0 +1,51 @@ +package com.ruoyi.cms.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 石河子岗位数据入库监测记录 shz.job_excel_import_record + */ +@Data +@ApiModel("石河子岗位数据入库监测记录") +@TableName("job_excel_import_record") +public class JobExcelImportRecord extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 监测主键ID */ + @Excel(name = "监测主键ID") + @ApiModelProperty("监测主键ID") + private String detectionId; + + /** 原始文件名称 */ + @Excel(name = "原始文件名称") + @ApiModelProperty("原始文件名称") + private String fileName; + + /** 入库数量 */ + @Excel(name = "入库数量") + @ApiModelProperty("入库数量") + private Integer storageNumber; + + /** 入库结果简述 */ + @Excel(name = "入库结果简述") + @ApiModelProperty("入库结果简述") + private String storageResult; + + /** 入库详情 */ + @Excel(name = "入库详情") + @ApiModelProperty("入库详情") + private String storageDetail; + + /** 失败原因 */ + @Excel(name = "失败原因") + @ApiModelProperty("失败原因") + private String failedReason; +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobShzLocalExcelVo.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobShzLocalExcelVo.java new file mode 100644 index 0000000..52c0730 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/vo/JobShzLocalExcelVo.java @@ -0,0 +1,146 @@ +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.*; +import com.ruoyi.common.annotation.Excel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * 石河子本地导入岗位对象 job + */ +@Data +public class JobShzLocalExcelVo +{ + + @ExcelProperty(value = "序号", index = 0) + @Excel(name = "序号") + @ApiModelProperty("序号") + private String xh; + + @ExcelProperty(value = "所属行业", index = 1) + @Excel(name = "所属行业") + @ApiModelProperty("所属行业") + private String industry; + + @ExcelProperty(value = "单位名称", index = 2) + @Excel(name = "单位名称") + @ApiModelProperty("单位名称") + private String companyName; + + @ExcelProperty(value = "统一社会信用代码", index = 3) + @ApiModelProperty("统一社会信用代码") + private String code; + + @ExcelProperty(value = "单位类型", index = 4) + @ApiModelProperty("单位类型") + private String companyNature; + + @ExcelProperty(value = "人员规模", index = 5) + @ApiModelProperty("人员规模") + private String scale; + + @ExcelProperty(value = "工作地区", index = 6) + @ApiModelProperty("工作地区") + private String jobAddress; + + @ExcelProperty(value = "岗位工种名称", index =7) + @Excel(name = "岗位工种名称") + @ApiModelProperty("岗位工种名称") + private String jobTitle; + + @ExcelProperty(value = "招聘人数", index = 8,converter = VacancyLongConverter.class) + @Excel(name = "招聘人数") + @ApiModelProperty("招聘人数") + private Long vacancies; + + @ExcelProperty(value = "任职要求", index = 9) + @ApiModelProperty("任职要求") + private String description; + + @Excel(name = "学历要求 对应字典education") + @ExcelProperty(value = "任职要求", index = 10) + @ApiModelProperty("学历要求 对应字典education") + private String education; + + @ExcelProperty(value = "工作性质", index = 11) + @ApiModelProperty("工作性质") + private String workNature; + + @ExcelProperty(value = "薪酬类型", index = 12) + @ApiModelProperty("薪酬类型") + private String salaryType; + + @ExcelProperty(value = "最小薪资", index = 13) + @Excel(name = "最小薪资", readConverterExp = "元=") + @ApiModelProperty("最小薪资(元)") + private Long minSalary; + + @ExcelProperty(value = "最大薪资", index = 14) + @Excel(name = "最大薪资", readConverterExp = "元=") + @ApiModelProperty("最大薪资(元)") + private Long maxSalary; + + @ExcelProperty(value = "工作经验", index = 15) + @Excel(name = "工作经验要求 对应字典experience") + @ApiModelProperty("工作经验要求 对应字典experience") + private String experience; + + @ExcelProperty(value = "专业要求", index = 16) + @Excel(name = "专业要求") + private String majorRequire; + + @ExcelProperty(value = "联系人对象", index = 17, converter = ContactParseConverter.class) + private ContactParseResult contactParseResult; + + @ExcelProperty(value = "邮箱", index = 18) + private String email; + + @ExcelProperty(value = "岗位收集人", index = 19) + @ApiModelProperty("岗位收集人") + private String gwsjr; + + @ExcelProperty(value = "需求月份", index = 20,converter = PostingDateConverter.class) + @JsonFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty("发布时间") + private Date postingDate; + + @ExcelProperty(value = "经办站点", index = 21) + @ApiModelProperty("数据来源/经办站点") + private String dataSource; + + @ExcelProperty(value = "联系人") + @ApiModelProperty("联系人") + private String contactPerson; + + @ExcelProperty(value = "联系人电话") + @ApiModelProperty("联系人电话") + private String contactPersonPhone; + + @ExcelProperty(value = "岗位区划") + @Excel(name = "岗位区划") + @ApiModelProperty("岗位区划") + private String jobLocation; + + @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; + + @ApiModelProperty("联系人列表") + private List jobContacts; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobExcelImportRecordMapper.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobExcelImportRecordMapper.java new file mode 100644 index 0000000..8e060ae --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobExcelImportRecordMapper.java @@ -0,0 +1,19 @@ +package com.ruoyi.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.cms.domain.JobExcelImportRecord; +import java.util.List; + +/** + * 石河子岗位数据入库监测记录Mapper + */ +public interface JobExcelImportRecordMapper extends BaseMapper { + + /** + * 查询石河子岗位数据入库监测记录列表 + * + * @param shzJobExcelImportRecord 石河子岗位数据入库监测记录 + * @return 石河子岗位数据入库监测记录集合 + */ + List selectShzJobExcelImportRecordList(JobExcelImportRecord shzJobExcelImportRecord); +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/JobExcelImportRecordService.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/JobExcelImportRecordService.java new file mode 100644 index 0000000..a375e3b --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/JobExcelImportRecordService.java @@ -0,0 +1,22 @@ +package com.ruoyi.cms.service; + +import com.ruoyi.cms.domain.JobExcelImportRecord; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 石河子岗位数据入库监测记录Service接口 + */ +public interface JobExcelImportRecordService { + + /** + * 查询石河子岗位数据入库监测记录列表 + * + * @param jobExcelImportRecord 石河子岗位数据入库监测记录 + * @return 石河子岗位数据入库监测记录集合 + */ + List selectShzJobExcelImportRecordList(JobExcelImportRecord jobExcelImportRecord); + + void uploadFile(MultipartFile file) throws Exception; +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobExcelImportRecordServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobExcelImportRecordServiceImpl.java new file mode 100644 index 0000000..559dfd2 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/JobExcelImportRecordServiceImpl.java @@ -0,0 +1,292 @@ +package com.ruoyi.cms.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.cms.domain.BussinessDictData; +import com.ruoyi.cms.domain.Job; +import com.ruoyi.cms.domain.JobContact; +import com.ruoyi.cms.domain.JobExcelImportRecord; +import com.ruoyi.cms.domain.query.CompanySearch; +import com.ruoyi.cms.domain.vo.ContactParseResult; +import com.ruoyi.cms.domain.vo.JobShzLocalExcelVo; +import com.ruoyi.cms.mapper.CompanyMapper; +import com.ruoyi.cms.mapper.JobExcelImportRecordMapper; +import com.ruoyi.cms.mapper.JobMapper; +import com.ruoyi.cms.service.*; +import com.ruoyi.cms.util.excel.JobShzLocalExcelListener; +import com.ruoyi.cms.util.excel.UpExcelDictUtil; +import com.ruoyi.common.core.domain.entity.Company; +import com.ruoyi.common.core.domain.entity.Industry; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 石河子岗位数据入库监测记录Service实现 + */ +@Service +public class JobExcelImportRecordServiceImpl extends ServiceImpl implements JobExcelImportRecordService { + + @Autowired + private JobExcelImportRecordMapper jobExcelImportRecordMapper; + @Autowired + private JobMapper jobMapper; + @Autowired + private CompanyMapper companyMapper; + @Autowired + private IBussinessDictTypeService iBussinessDictTypeService; + @Autowired + private IBussinessDictDataService iBussinessDictDataService; + @Autowired + private IIndustryService industryService; + @Autowired + private IJobService jobService; + + @Override + public List selectShzJobExcelImportRecordList(JobExcelImportRecord jobExcelImportRecord) { + return jobExcelImportRecordMapper.selectShzJobExcelImportRecordList(jobExcelImportRecord); + } + + @Override + public void uploadFile(MultipartFile file) throws Exception{ + String originalName = file.getOriginalFilename(); + String nameNoSuffix = FileNameUtil.getName(originalName); + //获取字典 + List workNaData = iBussinessDictTypeService.selectDictDataByType("work_nature"); + List majorRData = iBussinessDictTypeService.selectDictDataByType("major_require"); + List salarTypeData = iBussinessDictTypeService.selectDictDataByType("salary_type"); + List companyNatureData = iBussinessDictTypeService.selectDictDataByType("company_nature"); + List areaData = iBussinessDictTypeService.selectDictDataByType("area"); + //查询第一级 + List industries=industryService.selectFirstLevelIndustryList(); + + JobExcelImportRecord jobExcelImportRecord=new JobExcelImportRecord(); + jobExcelImportRecord.setFileName(nameNoSuffix); + String detectionId = IdUtils.fastSimpleUUID(); + jobExcelImportRecord.setDetectionId(detectionId); + List validDataList; + try (InputStream inputStream = file.getInputStream()){ + JobShzLocalExcelListener listener = new JobShzLocalExcelListener(); + EasyExcel.read(inputStream, JobShzLocalExcelVo.class, listener).extraRead(CellExtraTypeEnum.MERGE).sheet(0).headRowNumber(2).doRead(); + validDataList = listener.getValidDataList(); + jobExcelImportRecord.setStorageNumber(validDataList.size()); + } + if (CollUtil.isEmpty(validDataList)) { + jobExcelImportRecord.setStorageNumber(0); + jobExcelImportRecordMapper.insert(jobExcelImportRecord); + return; + } + jobExcelImportRecord.setStorageNumber(validDataList.size()); + + // 2.1 所有已存在企业 name -> companyId + Map companyNameIdMap = new HashMap<>(); + List allCompany = companyMapper.selectCompanyCleanList(new CompanySearch()); + if (CollUtil.isNotEmpty(allCompany)) { + allCompany.forEach(c -> companyNameIdMap.put(c.getName(), c.getCompanyId())); + } + + // 收集当前批次所有匹配key(只查需要的岗位,避免全量企业岗位加载上万条) + Set matchKeySet = new HashSet<>(); + for (JobShzLocalExcelVo vo : validDataList) { + String cName = vo.getCompanyName(); + String title = vo.getJobTitle(); + String key=buildFullMatchKey(cName,title,vo.getVacancies(),vo.getMinSalary(),vo.getMaxSalary()); + matchKeySet.add(key); + } + + List targetKeyList = new ArrayList<>(matchKeySet); + // 精准查询当前批次需要对比的存量岗位 + List existJobList = jobMapper.selectJobByMatchKeyList(targetKeyList); + Map existJobMap = new HashMap<>(); + for (Job job : existJobList) { + String key=buildFullMatchKey(job.getCompanyName(),job.getJobTitle(),job.getVacancies(),job.getMinSalary(),job.getMaxSalary()); + existJobMap.put(key, job); + } + + // 本批计数器 + List waitInsertCompany = new ArrayList<>(); + List waitInsertJob = new ArrayList<>(); + List waitUpdateJob = new ArrayList<>(); + + // 3. 批量容器 & 统计计数器 + int addCompanyNum = 0; + int addJobNum = 0; + int updateJobNum = 0; + + // 4. 循环解析每行Excel数据 + for (JobShzLocalExcelVo excelVo : validDataList) { + Job job = new Job(); + BeanUtil.copyProperties(excelVo, job); + // 岗位固定默认值 + job.setIsPublish(1); + job.setView(0L); + job.setApplyNum(0); + job.setCreateBy(detectionId); + job.setJobLocation(job.getJobAddress()); + job.setExperience("0"); + job.setJobType("0"); + job.setJobStatus("0"); + job.setDataSource("5"); + + String companyName = excelVo.getCompanyName(); + String creditCode = excelVo.getCode(); + Long companyId = null; + + // ========== 企业处理逻辑 ========== + if (StringUtils.isNotBlank(companyName)) { + if (companyNameIdMap.containsKey(companyName)) { + // 企业已存在 + companyId = companyNameIdMap.get(companyName); + } else { + // 新增企业 + Company newCompany = new Company(); + newCompany.setCreateBy(detectionId); + newCompany.setCompanyStatus("0"); + newCompany.setCode(creditCode); + newCompany.setName(companyName); + // 企业字典转换 + newCompany.setIndustry(UpExcelDictUtil.getIndustry(industries, excelVo.getIndustry())); + String companyNature = iBussinessDictDataService.findCode(companyNatureData, excelVo.getCompanyNature()); + newCompany.setCompanyNature(StringUtils.isNotBlank(companyNature) ? companyNature : "9"); + String scaleKey = UpExcelDictUtil.KEY_TO_SCALE_KEY.get(excelVo.getScale()); + newCompany.setScale(StringUtils.isNotBlank(scaleKey) ? scaleKey : "0"); + + waitInsertCompany.add(newCompany); + addCompanyNum++; + companyId = -1L; // 临时占位,批量插入后回填真实ID + } + } + job.setCompanyId(companyId); + + // ========== 岗位字典赋值 ========== + String eduKey = UpExcelDictUtil.KEY_TO_SCALE_KEY.get(excelVo.getEducation()); + job.setEducation(StringUtils.isNotBlank(eduKey) ? eduKey : "-1"); + String expKey = UpExcelDictUtil.KEY_TO_EXPERIENCE_KEY.get(excelVo.getExperience()); + job.setExperience(StringUtils.isNotBlank(expKey) ? expKey : "0"); + job.setWorkNature(iBussinessDictDataService.findCode(workNaData, excelVo.getWorkNature())); + job.setSalaryType(iBussinessDictDataService.findCode(salarTypeData, excelVo.getSalaryType())); + job.setMajorRequire(iBussinessDictDataService.findCode(majorRData, excelVo.getMajorRequire())); + job.setJobLocationAreaCode(UpExcelDictUtil.matchRegionCode(areaData, excelVo.getJobAddress())); + + // ========== 联系人解析 ========== + List contactList = new ArrayList<>(); + ContactParseResult parseResult = excelVo.getContactParseResult(); + if (ObjectUtil.isNotNull(parseResult) && CollUtil.isNotEmpty(parseResult.getContactItemList())) { + parseResult.getContactItemList().stream() + .filter(item -> StringUtils.isNotBlank(StrUtil.trim(item.getPhone()))) + .forEach(item -> { + JobContact contact = new JobContact(); + contact.setContactPerson(companyName); + contact.setContactPersonPhone(StrUtil.trim(item.getPhone())); + contact.setRemark(item.getRemark()); + contactList.add(contact); + }); + } + if (CollUtil.isNotEmpty(contactList)) { + job.setJobContactList(contactList); + } + + // ========== 判断岗位新增/更新(兼容历史无companyId数据) ========== + String matchKey=buildFullMatchKey(excelVo.getCompanyName(), excelVo.getJobTitle(),excelVo.getVacancies(), excelVo.getMinSalary(), excelVo.getMaxSalary()); + if (existJobMap.containsKey(matchKey)) { + // 命中存量岗位(含历史无企业ID脏数据) + Job oldJob = existJobMap.get(matchKey); + job.setJobId(oldJob.getJobId()); + waitUpdateJob.add(job); + updateJobNum++; + } else { + // 全新岗位 + waitInsertJob.add(job); + addJobNum++; + } + } + //批量入库 + if (CollUtil.isNotEmpty(waitInsertCompany)) { + companyMapper.batchInsert(waitInsertCompany); + // 重新查询新增企业,刷新name-id映射 + List newCompanyNames = waitInsertCompany.stream() + .map(Company::getName) + .collect(Collectors.toList()); + List newInsertCompanies = companyMapper.selectCompanyByNameList(newCompanyNames); + newInsertCompanies.forEach(c -> companyNameIdMap.put(c.getName(), c.getCompanyId())); + + // 给待新增岗位回填真实companyId + for (Job j : waitInsertJob) { + if (j.getCompanyId() != null && j.getCompanyId() == -1L) { + Long realCid = companyNameIdMap.get(j.getCompanyName()); + j.setCompanyId(realCid); + } + } + // 给历史待更新岗位补全companyId(存量数据标准化) + for (Job j : waitUpdateJob) { + if (j.getCompanyId() == null && companyNameIdMap.containsKey(j.getCompanyName())) { + j.setCompanyId(companyNameIdMap.get(j.getCompanyName())); + } + } + } + + //批量新增岗位 + if (CollUtil.isNotEmpty(waitInsertJob)) { + jobMapper.batchInsert(waitInsertJob); + } + //批量更新岗位 + if (CollUtil.isNotEmpty(waitUpdateJob)) { + List> splitList = CollUtil.split(waitUpdateJob, 500); + for (List sub : splitList) { + sub.forEach(it -> { + it.setCreateBy(null); + it.setCreateTime(null); + it.setUpdateBy(detectionId); + jobMapper.updateById(it); + } + ); + } + } + + //记录结果 + String brief = String.format("共导入%d条数据,新增企业%d家,新增岗位%d条,更新岗位%d条", + jobExcelImportRecord.getStorageNumber(), addCompanyNum, addJobNum, updateJobNum); + jobExcelImportRecord.setStorageResult(brief); + // 入库详情 + StringBuilder detailSb = new StringBuilder(); + detailSb.append("【导入统计明细】\n"); + detailSb.append("有效导入行数:").append(jobExcelImportRecord.getStorageNumber()).append("\n"); + detailSb.append("新增企业:").append(addCompanyNum).append("家\n"); + detailSb.append("新增岗位:").append(addJobNum).append("条\n"); + detailSb.append("更新岗位:").append(updateJobNum).append("条"); + jobExcelImportRecord.setStorageDetail(detailSb.toString()); + jobExcelImportRecordMapper.insert(jobExcelImportRecord); + + //更新坐标并且刷选es + jobService.updateLon(); + } + + /** + * 获取key + * @param companyName + * @param jobTitle + * @param vacancies + * @param minSalary + * @param maxSalary + * @return + */ + private String buildFullMatchKey(String companyName, String jobTitle, Long vacancies, Long minSalary, Long maxSalary) { + String vacStr = vacancies == null ? "" : vacancies.toString(); + String minSalStr = minSalary == null ? "" : minSalary.toString(); + String maxSalStr = maxSalary == null ? "" : maxSalary.toString(); + return companyName + "|" + jobTitle + "|" + vacStr + "|" + minSalStr + "|" + maxSalStr; + } + +} \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobShzLocalExcelListener.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobShzLocalExcelListener.java new file mode 100644 index 0000000..ce41a9b --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/JobShzLocalExcelListener.java @@ -0,0 +1,161 @@ +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.exception.ExcelDataConvertException; +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.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 企业岗位excel监听 + */ +@Getter +public class JobShzLocalExcelListener extends AnalysisEventListener { + + private final List validDataList = new ArrayList<>(); + private final List mergeCellList = new ArrayList<>(); + private final Map columnFieldMap = new HashMap<>(); + private String columnErrorMsg; + + public JobShzLocalExcelListener() { + 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(JobShzLocalExcelVo jobExcelVo, AnalysisContext analysisContext) { + //处理联系方式 + 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); + } + } + + @Override + public void onException(Exception exception, AnalysisContext context) { + if (exception instanceof ExcelDataConvertException) { + ExcelDataConvertException e = (ExcelDataConvertException) exception; + int realExcelRow = e.getRowIndex() + 3; + int colNum = e.getColumnIndex() + 1; + String cellText = e.getCellData().getStringValue(); + System.out.println("Excel导入失败:实际行号" + realExcelRow + ",列号" + colNum + ",单元格内容【" + cellText + "】"); + } + } + + 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/UpExcelDictUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/UpExcelDictUtil.java new file mode 100644 index 0000000..55c8b22 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/excel/UpExcelDictUtil.java @@ -0,0 +1,136 @@ +package com.ruoyi.cms.util.excel; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.cms.domain.BussinessDictData; +import com.ruoyi.common.core.domain.entity.Industry; + +import java.util.*; + +public class UpExcelDictUtil { + /** + * 工作经验集合 + * 1,应届毕业生 + * 11,10年以上 + * 2,1年 + * 20,20年以上 + * 3,2年 + * 4,3-4年 + * 6,5-7年 + * 80,在读生 + * 9,8-9年 + * 90,不限 + */ + public static final Map KEY_TO_EXPERIENCE_KEY = new HashMap<>(); + + /** + * 学历 + * 11博士 + * 14硕士 + * 21本科 + * 31专科 + * 41中专 + * 61高中 + * 71初中 + * 81小学 + * 90学历不限 + */ + public static final Map KEY_TO_EDUCATION_KEY = new HashMap<>(); + + /** + * 规模 + * 1_3000人以上 + * 2_1000--2999人 + * 3_500--999人 + * 4_100--499人 + * 5_10--99人 + * 6_10人以下 + * 9_其他 + */ + public static final Map KEY_TO_SCALE_KEY = new HashMap<>(); + + //默认石河子市 + private static final Integer SHIHEZI_DEFAULT_CODE = 0; + + static { + //工作经验 + KEY_TO_EXPERIENCE_KEY.put("应届毕业生","2"); + KEY_TO_EXPERIENCE_KEY.put("在读生","10"); + KEY_TO_EXPERIENCE_KEY.put("1年","4"); + KEY_TO_EXPERIENCE_KEY.put("2年","4"); + KEY_TO_EXPERIENCE_KEY.put("3-4年","5"); + KEY_TO_EXPERIENCE_KEY.put("5-7年","6"); + KEY_TO_EXPERIENCE_KEY.put("8-9年","6"); + KEY_TO_EXPERIENCE_KEY.put("10年以上","7"); + KEY_TO_EXPERIENCE_KEY.put("20年以上","8"); + KEY_TO_EXPERIENCE_KEY.put("不限","0"); + + //学历 + KEY_TO_EDUCATION_KEY.put("博士","6"); + KEY_TO_EDUCATION_KEY.put("硕士","5"); + KEY_TO_EDUCATION_KEY.put("本科","4"); + KEY_TO_EDUCATION_KEY.put("专科","3"); + KEY_TO_EDUCATION_KEY.put("中专","1"); + KEY_TO_EDUCATION_KEY.put("高中","2"); + KEY_TO_EDUCATION_KEY.put("初中","1"); + KEY_TO_EDUCATION_KEY.put("小学","0"); + KEY_TO_EDUCATION_KEY.put("学历不限","-1"); + + //规模 + KEY_TO_SCALE_KEY.put("3000人以上","6"); + KEY_TO_SCALE_KEY.put("1000--2999人","6"); + KEY_TO_SCALE_KEY.put("500--999人","5"); + KEY_TO_SCALE_KEY.put("100--499人","3"); + KEY_TO_SCALE_KEY.put("10--99人","2"); + KEY_TO_SCALE_KEY.put("10人以下","1"); + KEY_TO_SCALE_KEY.put("其他","0"); + } + + /** + * 获取行业 + * @param industryList + * @param name + * @return + */ + public static String getIndustry(List industryList,String name){ + if (Objects.isNull(industryList) || StrUtil.isBlank(name)) { + return null; + } + String targetName = name.trim(); + for(Industry industry:industryList){ + String industryName = StrUtil.trim(industry.getIndustryName()); + if(Objects.equals(industryName, targetName)){ + return String.valueOf(industry.getIndustryId()); + } + } + return null; + } + + /** + * 获取区划 + * @param dictDataList + * @param address + * @return + */ + public static Integer matchRegionCode(List dictDataList, String address) { + // 空参数直接返回默认值 + if (StrUtil.isBlank(address) || dictDataList == null || dictDataList.isEmpty()) { + return SHIHEZI_DEFAULT_CODE; + } + String addr = address.trim(); + + // 重点:按标签长度倒序,长关键词优先匹配,防止短词误命中 + Optional matchDict = dictDataList.stream() + .filter(d -> StrUtil.isNotBlank(d.getDictLabel())) + .sorted((o1, o2) -> Integer.compare(o2.getDictLabel().length(), o1.getDictLabel().length())) + .filter(d -> addr.contains(d.getDictLabel())) + .findFirst(); + + return matchDict.map(BussinessDictData::getDictValue).map(val -> { + try { + return Integer.valueOf(val); + } catch (NumberFormatException e) { + return null; + } + }).orElse(SHIHEZI_DEFAULT_CODE); + } +} diff --git a/ruoyi-bussiness/src/main/resources/mapper/app/JobExcelImportRecordMapper.xml b/ruoyi-bussiness/src/main/resources/mapper/app/JobExcelImportRecordMapper.xml new file mode 100644 index 0000000..524f6c3 --- /dev/null +++ b/ruoyi-bussiness/src/main/resources/mapper/app/JobExcelImportRecordMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + select detection_id, + file_name, + storage_number, + storage_result, + storage_detail, + failed_reason, + del_flag, + create_by, + create_time, + update_by, + update_time, + remark + from job_excel_import_record + + + + +