weaver-hrm-recruit/src/com/engine/recruit/service/impl/ResumeRecognitionServiceImp...

623 lines
24 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.engine.recruit.service.impl;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.engine.core.impl.Service;
import com.engine.recruit.conn.*;
import com.engine.recruit.constant.RecruitConstant;
import com.engine.recruit.enums.ApplicationStatusEnum;
import com.engine.recruit.exception.CustomizeRunTimeException;
import com.engine.recruit.service.ResumeRecognitionService;
import com.weaver.formmodel.data.model.Formfield;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import weaver.common.DateUtil;
import weaver.conn.RecordSet;
import weaver.file.ImageFileManager;
import weaver.formmode.recruit.modeexpand.util.RecruitModeUtil;
import weaver.general.BaseBean;
import weaver.general.Util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author:dxfeng
* @createTime: 2023/12/12
* @version: 1.0
*/
public class ResumeRecognitionServiceImpl extends Service implements ResumeRecognitionService {
@Override
public Map<String, Object> resumeUpload(Map<String, Object> param) {
Map<String, Object> returnMap = new HashMap<>(16);
String resumeId = Util.null2String(param.get("resumeId"));
int imageField = ApplicantCommonInfo.getImageFieldByDocId(resumeId);
if (-1 == imageField) {
throw new CustomizeRunTimeException("原始简历文件获取失败,请重新上传原始简历");
}
qllResumeUpload(imageField, returnMap, true);
returnMap.put("isOcr", true);
return returnMap;
}
@Override
public Map<String, Object> importResume(Map<String, Object> param) {
Map<String, Object> returnMap = new HashMap<>();
String resumeId = Util.null2String(param.get("resumeId"));
Map<String, Object> uploadDataMap = new HashMap<>();
qllResumeUpload(Convert.toInt(resumeId), uploadDataMap, false);
Map<String, Object> resumeData = (Map<String, Object>) uploadDataMap.get("data");
// 添加原始附件字段信息
int secCategory = Convert.toInt(RecruitConstant.APPLICANTS_RESUMES_CATEGORY);
try {
int docId = RecruitModeUtil.createDocId(secCategory, Convert.toInt(resumeId), user);
resumeData.put("ysjl", docId);
} catch (Exception e) {
throw new RuntimeException(e);
}
List<RecruitDataMap<Object>> studyList = (List<RecruitDataMap<Object>>) resumeData.remove("jyjl");
List<RecruitDataMap<Object>> workList = (List<RecruitDataMap<Object>>) resumeData.remove("gzjl");
List<RecruitDataMap<Object>> projectList = (List<RecruitDataMap<Object>>) resumeData.remove("xmjy");
// 状态
resumeData.put("zt", ApplicationStatusEnum.DISTRIBUTION.getValue());
List<Formfield> fieldList = RecruitModeUtil.getFieldList("uf_jcl_yppc");
Set<String> keySet = fieldList.stream().map(Formfield::getFieldname).collect(Collectors.toSet());
// 移除不在 keySet 中的键值对
resumeData.entrySet().removeIf(entry -> !keySet.contains(entry.getKey()));
// 判断简历信息
CheckRepeatResume instance = CheckRepeatResume.getInstance();
Map<String, Object> map = instance.insertResumeAndReturn(resumeData);
String mainId = Util.null2String(map.get("mainId"));
String sourceId = Util.null2String(map.get("sourceId"));
returnMap.put("mainId", mainId);
returnMap.put("sourceId", sourceId);
returnMap.put("id", StringUtils.isNotBlank(sourceId) ? sourceId : mainId);
instance.insertResumeDetailTable(studyList, "uf_jcl_yppc_dt1", mainId, sourceId);
instance.insertResumeDetailTable(workList, "uf_jcl_yppc_dt2", mainId, sourceId);
instance.insertResumeDetailTable(projectList, "uf_jcl_yppc_dt3", mainId, sourceId);
return returnMap;
}
@Override
public Map<String, Object> fetchResume(Map<String, Object> param) {
String id = Util.null2String(param.get("id"));
String sql = "select * from uf_jcl_yppc where id = ? ";
RecordSet rs = new RecordSet();
rs.executeQuery(sql, id);
Map<String, Object> dataMap = RecruitRecordSet.getSingleRecordMap(rs);
List<Formfield> fieldList = RecruitModeUtil.getFieldList("uf_jcl_yppc");
Map<String, Formfield> fieldMap = fieldList.stream().collect(Collectors.toMap(Formfield::getFieldname, item -> item, (k1, k2) -> k1));
for (String key : dataMap.keySet()) {
Object value = dataMap.get(key);
if (StringUtils.isBlank(Convert.toStr(value))) {
continue;
}
// 计算新的value值
Formfield formfield = fieldMap.get(key);
value = RecruitModeUtil.getFieldShowName(formfield, Convert.toStr(value)).replaceAll("<[^>]*>", "");
// 更新value值
dataMap.put(key, value);
}
return dataMap;
}
/**
* @param resumeId 简历ID
* @param returnMap 响应集合
*/
private void qllResumeUpload(int resumeId, Map<String, Object> returnMap, boolean isCard) {
String response = doQllPost(resumeId);
if (StringUtils.isBlank(response)) {
throw new CustomizeRunTimeException("千里聆接口调用失败,响应结果为空");
}
BaseBean baseBean = new BaseBean();
JSONObject all = JSONObject.parseObject(response);
if (!all.getBoolean("isSuccess")) {
baseBean.writeLog("千里聆响应数据:" + all);
throw new CustomizeRunTimeException(all.getString("errorMsg"));
}
JSONObject resultall = all.getJSONObject("data");
String status = resultall.getString("state");
if ("fail".equals(status)) {
throw new CustomizeRunTimeException("调用千里聆接口失败,失败原因:" + resultall.getString("info"));
}
baseBean.writeLog("千里聆OCR解析数据" + resultall);
JSONObject result = resultall.getJSONObject("result");
Map<String, Object> dataMap = parseQllJsonToMapV2(result, isCard);
// 解析图片信息
String face_base64 = resultall.getString("face_base64");
if (StringUtils.isNotBlank(face_base64)) {
try {
byte[] decodedBytes = Base64.getDecoder().decode(face_base64);
InputStream inputStream = new ByteArrayInputStream(decodedBytes);
int imageFileId = RecruitModeUtil.generateImageFileId(inputStream, dataMap.get("xm") + "-" + UUID.randomUUID() + ".png");
int docId = RecruitModeUtil.createDocId(Convert.toInt(RecruitConstant.APPLICANTS_RESUMES_CATEGORY), imageFileId, user);
dataMap.put("jlzp", docId);
} catch (Exception e) {
baseBean.writeLog("千里聆OCR简历照片解析失败" + e);
}
}
returnMap.put("data", dataMap);
}
/**
* 千里聆解析字段内容处理(V2)
*
* @param obj
* @return
*/
private Map<String, Object> parseQllJsonToMapV2(JSONObject obj, boolean isCard) {
Map<String, Object> dataMap = new HashMap<>(16);
JSONObject personalInformation = obj.getJSONObject("个人信息");
// 投递时间
dataMap.put("tdsj", DateUtil.getDateTime());
// 姓名
String xm = personalInformation.getString("姓名");
dataMap.put("xm", xm);
// 电子邮箱
String dzyx = personalInformation.getString("电子邮箱");
dataMap.put("dzyx", dzyx);
// 手机号码
String sjhm = personalInformation.getString("手机号");
dataMap.put("sjhm", sjhm);
// 年龄
String nl = personalInformation.getString("年龄");
if (StringUtils.isNotBlank(nl)) {
dataMap.put("nl", nl);
}
// 出生日期
String csrq = personalInformation.getString("出生日期");
dataMap.put("csrq", parseDateObject(csrq));
// 性别 默认为男
String xb = personalInformation.getString("性别");
if ("".equals(xb)) {
dataMap.put("xb", 1);
} else if ("".equals(xb)) {
dataMap.put("xb", 0);
} else {
dataMap.put("xb", null);
}
// 体重KG
String tz = personalInformation.getString("体重");
dataMap.put("tzkg", Convert.toDouble(tz));
// 身高CM
String sg = personalInformation.getString("身高");
dataMap.put("sgcm", Convert.toDouble(sg));
// 籍贯(字段类型不支持)
String jg = personalInformation.getString("籍贯");
//dataMap.put("jg", parseArray(obj.getJSONArray("籍贯")));
// 婚姻状况
List<Map<String, String>> hyzk = getBrowserArray(personalInformation.getString("婚姻状况"), ModeBrowserCommonInfo.TYPE_MARITAL_STATUS);
if (CollectionUtils.isNotEmpty(hyzk)) {
dataMap.put("hyzk", hyzk);
if (!isCard) {
dataMap.put("hyzk", hyzk.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 当前所在地(字段类型不支持)
String jdz = personalInformation.getString("现居住地");
//dataMap.put("jzd", parseArray(obj.getJSONArray("现居住地")));
// 政治面貌
List<Map<String, String>> zzmm = getBrowserArray(personalInformation.getString("政治面貌"), ModeBrowserCommonInfo.TYPE_POLITICAL_LANDSCAPE);
if (CollectionUtils.isNotEmpty(zzmm)) {
dataMap.put("zzmm", zzmm);
if (!isCard) {
dataMap.put("zzmm", zzmm.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 在职状态
List<Map<String, String>> zzzt = getBrowserArray(personalInformation.getString("在职状态"), ModeBrowserCommonInfo.TYPE_ON_THE_JOB_STATUS);
if (CollectionUtils.isNotEmpty(zzzt)) {
dataMap.put("zzzt", zzzt);
if (!isCard) {
dataMap.put("zzzt", zzzt.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 工作经验
String gzjyStr = personalInformation.getString("工作经验");
if (StringUtils.isNotBlank(gzjyStr)) {
if (gzjyStr.contains("")) {
String[] split = gzjyStr.split("");
if (split.length == 2) {
gzjyStr = split[1];
}
}
}
List<Map<String, String>> gzjy = getBrowserArray(gzjyStr, ModeBrowserCommonInfo.TYPE_WORK_EXPERIENCE);
if (CollectionUtils.isNotEmpty(gzjy)) {
dataMap.put("gzjy", gzjy);
if (!isCard) {
dataMap.put("gzjy", gzjy.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 最高学位
List<Map<String, String>> zgxw = getBrowserArray(personalInformation.getString("最高学位"), ModeBrowserCommonInfo.TYPE_DEGREE);
if (CollectionUtils.isNotEmpty(zgxw)) {
dataMap.put("zgxw", zgxw);
if (!isCard) {
dataMap.put("zgxw", zgxw.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 最高学历
String highestEduLevel = personalInformation.getString("最高学历");
List<Map<String, String>> zgxl = getBrowserArray(highestEduLevel, this::getEducationLevelArray);
if (CollectionUtils.isNotEmpty(zgxl)) {
dataMap.put("zgxl", zgxl);
if (!isCard) {
dataMap.put("zgxl", zgxl.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
}
// 毕业院校
String byyx = personalInformation.getString("毕业院校");
dataMap.put("byyx", byyx);
// 现税前月薪K
// 自我评价
String zwpj = personalInformation.getString("个人评价");
dataMap.put("zwpj", zwpj);
// 民族
dataMap.put("mz", personalInformation.getString("民族"));
JSONObject jobSearchInformation = obj.getJSONObject("求职信息");
// 期望税前月薪K
String qwxz = jobSearchInformation.getString("期望薪资");
dataMap.put("qwxz", qwxz);
// 明细表数据
// 教育经历
JSONArray jyjl = obj.getJSONArray("学业信息");
List<RecruitDataMap<Object>> studyList = new ArrayList<>();
if (null != jyjl && jyjl.size() > 0) {
for (int i = 0; i < jyjl.size(); i++) {
JSONObject o = (JSONObject) jyjl.get(i);
String studyDate = o.getString("就读时期");
RecruitDataMap<Object> studyMap = new RecruitDataMap<>();
studyMap.putAll(getDateRange(studyDate, true));
String schoolName = o.getString("学校名称");
studyMap.put("xxmc", schoolName);
String educationLevel = o.getString("学历");
List<Map<String, String>> xl = getBrowserArray(educationLevel, this::getEducationLevelArray);
studyMap.put("xl", xl);
if (!isCard && CollectionUtils.isNotEmpty(xl)) {
studyMap.put("xl", xl.stream().map(item -> item.get("id")).collect(Collectors.joining(",")));
}
String professionalName = o.getString("专业名称");
studyMap.put("zy", professionalName);
// 主表 专业字段
if (byyx.equals(schoolName) && highestEduLevel.equals(educationLevel)) {
dataMap.put("zy", professionalName);
}
studyList.add(studyMap);
}
}
dataMap.put("jyjl", studyList);
// 工作经历
JSONArray gzjl = obj.getJSONArray("工作经历");
List<RecruitDataMap<Object>> workList = new ArrayList<>();
if (null != gzjl && gzjl.size() > 0) {
for (int i = 0; i < gzjl.size(); i++) {
JSONObject o = (JSONObject) gzjl.get(i);
String workDate = o.getString("工作时间");
RecruitDataMap<Object> workMap = new RecruitDataMap<>();
workMap.putAll(getDateRange(workDate, false));
workMap.put("gsmc", o.getString("工作单位"));
workMap.put("gw", o.getString("岗位名称"));
workMap.put("gzzz", o.getString("工作内容"));
workList.add(workMap);
}
}
dataMap.put("gzjl", workList);
// 项目经验
JSONArray xmjy = obj.getJSONArray("项目经验");
List<RecruitDataMap<Object>> projectList = new ArrayList<>();
if (null != xmjy && xmjy.size() > 0) {
for (int i = 0; i < xmjy.size(); i++) {
JSONObject o = (JSONObject) xmjy.get(i);
String startDate = getFormatDate(o.getString("开始时间"));
String endDate = getFormatDate(o.getString("结束时间"));
RecruitDataMap<Object> projectMap = new RecruitDataMap<>();
projectMap.put("kssj", startDate);
projectMap.put("jssj", endDate);
projectMap.put("xmmc", o.getString("项目名称"));
projectMap.put("drjs", o.getString("担任角色"));
projectMap.put("xmms", o.getString("项目描述"));
projectList.add(projectMap);
}
}
dataMap.put("xmjy", projectList);
return dataMap;
}
/**
* @param resumeId
* @return
*/
private String doQllPost(int resumeId) {
String url = RecruitConstant.OCR_URL;
String appId = RecruitConstant.APP_ID;
String appSecret = RecruitConstant.APP_SECRET;
if (StringUtils.isAnyBlank(url, appId, appSecret)) {
throw new CustomizeRunTimeException("千里聆配置出错,请检查配置文件");
}
long currentTime = System.currentTimeMillis();
ImageFileManager manager = new ImageFileManager();
manager.getImageFileInfoById(resumeId);
HttpPost postRequest = new HttpPost(url);
postRequest.addHeader("sign", getSign(appId, appSecret, currentTime));
postRequest.addHeader("appId", appId);
postRequest.addHeader("timestamp", String.valueOf(currentTime));
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
String fileName = manager.getImageFileName();
builder.addTextBody("type", fileName.substring(fileName.lastIndexOf('.') + 1));
// 对接新版抽取服务
builder.addTextBody("version", "V2");
builder.addBinaryBody("resume", manager.getInputStream(), ContentType.APPLICATION_OCTET_STREAM, manager.getImageFileName());
HttpEntity entity = builder.build();
postRequest.setEntity(entity);
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(postRequest);
HttpEntity responseEntity = response.getEntity();
return EntityUtils.toString(responseEntity);
} catch (Exception e) {
throw new CustomizeRunTimeException(e);
}
}
/**
* 解析头像
*
* @param faceUrl
* @param filename
* @param dataMap
*/
private void getImageFile(String faceUrl, String filename, Map<String, Object> dataMap) {
String appId = RecruitConstant.APP_ID;
String appSecret = RecruitConstant.APP_SECRET;
long currentTime = System.currentTimeMillis();
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(faceUrl);
httpGet.addHeader("sign", getSign(appId, appSecret, currentTime));
httpGet.addHeader("appId", appId);
httpGet.addHeader("timestamp", String.valueOf(currentTime));
// 执行请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 检查响应状态
if (response.getStatusLine().getStatusCode() == 200) {
// 获取响应实体
InputStream inputStream = response.getEntity().getContent();
int imageFileId = RecruitModeUtil.generateImageFileId(inputStream, filename + ".png");
int docId = RecruitModeUtil.createDocId(Convert.toInt(RecruitConstant.APPLICANTS_RESUMES_CATEGORY), imageFileId, user);
dataMap.put("jlzp", docId);
}
} catch (Exception e) {
throw new CustomizeRunTimeException(e);
}
}
/**
* 千里聆签名
*
* @param appId 开发者AppId
* @param appSecret 开发者appSecret
* @param timestamp 当前时间戳(毫秒数)
* @return
*/
private static String getSign(String appId, String appSecret, long timestamp) {
if (appId != null && appId.length() != 0) {
if (appSecret != null && appSecret.length() != 0) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(appId.getBytes());
md5.update((timestamp + "").getBytes());
md5.update(appSecret.getBytes());
byte[] bytes = md5.digest();
return (new BigInteger(1, bytes)).toString(16);
} catch (NoSuchAlgorithmException var8) {
throw new CustomizeRunTimeException("不支持的加密算法", var8);
}
} else {
throw new CustomizeRunTimeException("appSecret不能为空");
}
} else {
throw new CustomizeRunTimeException("appId不能为空");
}
}
/**
* 转换日期对象
*
* @param value
* @return
*/
private String parseDateObject(String value) {
if (StringUtils.isBlank(value)) {
return null;
}
value = value.replace(".", "-");
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
try {
sdf.parse(value);
} catch (Exception e) {
new BaseBean().writeLog("日期格式化失败", e);
value = null;
}
return value;
}
/**
* 获取开始时间和结束时间
*
* @param date
* @return
*/
private RecruitDataMap<Object> getDateRange(String date, boolean isStudy) {
RecruitDataMap<Object> dataRangeMap = new RecruitDataMap();
if (StringUtils.isBlank(date)) {
return dataRangeMap;
} else {
String[] split = date.split("-");
String end;
if (split.length > 0) {
end = getFormatDate(split[0]);
if (end.length() == 4) {
if (isStudy) {
end = end + "-09-01";
} else {
end = "";
}
}
dataRangeMap.put("kssj", end);
}
if (split.length > 1) {
end = getFormatDate(split[1]);
if (end.length() == 4) {
if (isStudy) {
end = end + "-07-01";
} else {
end = "";
}
}
dataRangeMap.put("jssj", end);
}
return dataRangeMap;
}
}
/**
* 获取yyyy-MM-dd时间格式日期
*
* @param dateStr
* @return
*/
private String getFormatDate(String dateStr) {
dateStr = dateStr.replace(".", "-").replace("\\/", "-");
if (dateStr.length() == 7) {
return dateStr + "-01";
} else if (dateStr.length() == 10) {
return dateStr;
} else if (dateStr.length() == 4) {
return dateStr;
}
return "";
}
/**
* 构建浏览按钮字段信息格式
*
* @param text
* @param fun
* @return
*/
private List<Map<String, String>> getBrowserArray(String text, Function<String, Map<String, String>> fun) {
List<Map<String, String>> list = new ArrayList<>();
if (StringUtils.isNotBlank(text)) {
String[] split = text.split(",");
for (String s : split) {
Map<String, String> apply = fun.apply(s);
list.add(apply);
}
}
return list;
}
/**
* 构建浏览按钮字段信息格式
*
* @param text
* @param browserType
* @return
*/
private List<Map<String, String>> getBrowserArray(String text, String browserType) {
List<Map<String, String>> list = new ArrayList<>();
if (StringUtils.isNotBlank(text)) {
String[] split = text.split(",");
for (String s : split) {
Map<String, String> map = new HashMap<>(2);
if (StringUtils.isNotBlank(text)) {
String id = ModeBrowserCommonInfo.getBrowserId(browserType, text);
if (StringUtils.isNotBlank(id)) {
map.put("id", id);
map.put("name", text);
}
}
list.add(map);
}
}
return list;
}
/**
* 构建学历字段信息
*
* @param text
* @return
*/
private Map<String, String> getEducationLevelArray(String text) {
Map<String, String> map = new HashMap<>(2);
if (StringUtils.isNotBlank(text)) {
String id = ModeBrowserCommonInfo.getEducationLevelId(text);
if (StringUtils.isNotBlank(id)) {
map.put("id", id);
map.put("name", text);
}
}
return map;
}
}