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

583 lines
21 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 com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.engine.core.exception.ECException;
import com.engine.core.impl.Service;
import com.engine.recruit.conn.ApplicantCommonInfo;
import com.engine.recruit.conn.ModeBrowserCommonInfo;
import com.engine.recruit.exception.CustomizeRunTimeException;
import com.engine.recruit.service.ResumeRecognitionService;
import com.engine.recruit.util.HttpUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
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.file.ImageFileManager;
import weaver.formmode.recruit.modeexpand.util.RecruitModeUtil;
import weaver.general.BaseBean;
import weaver.general.Util;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @author:dxfeng
* @createTime: 2023/12/12
* @version: 1.0
*/
public class ResumeRecognitionServiceImpl extends Service implements ResumeRecognitionService {
/**
* 简历识别类型
*/
private static final String OCR_TYPE = RecruitModeUtil.getRecruitPropValue("OCR_TYPE");
@Override
public Map<String, Object> resumeUpload(Map<String, Object> param) {
Map<String, Object> returnMap = new HashMap<>(16);
returnMap.put("isOcr", true);
if (StringUtils.isBlank(OCR_TYPE)) {
returnMap.put("isOcr", false);
return returnMap;
}
String resumeId = Util.null2String(param.get("resumeId"));
int imageField = ApplicantCommonInfo.getImageFieldByDocId(resumeId);
if (-1 == imageField) {
throw new CustomizeRunTimeException("原始简历文件获取失败,请重新上传原始简历");
}
// 调用千里聆服务
if ("1".equals(OCR_TYPE)) {
qllResumeUpload(imageField, returnMap);
}
// 调用阿里云服务
if ("2".equals(OCR_TYPE)) {
alyResumeUpload(imageField, returnMap);
}
return returnMap;
}
/**
* @param resumeId 简历ID
* @param returnMap 响应集合
*/
private void qllResumeUpload(int resumeId, Map<String, Object> returnMap) {
String response = doQllPost(resumeId);
if (StringUtils.isBlank(response)) {
throw new CustomizeRunTimeException("千里聆接口调用失败,响应结果为空");
}
JSONObject all = JSONObject.parseObject(response);
if (!all.getBoolean("isSuccess")) {
throw new CustomizeRunTimeException("千里聆接口调用失败,接口不通");
}
JSONObject resultall = all.getJSONObject("data");
String status = resultall.getString("state");
if ("fail".equals(status)) {
throw new CustomizeRunTimeException("调用千里聆接口失败,失败原因:" + resultall.getString("info"));
}
JSONObject result = resultall.getJSONObject("result");
Map<String, Object> dataMap = parseQllJsonToMap(result);
returnMap.put("data", dataMap);
}
/**
* @param resumeId 简历ID
* @param returnMap 响应集合
*/
private void alyResumeUpload(int resumeId, Map<String, Object> returnMap) {
String response = doAlyPost(resumeId);
JSONObject all = JSONObject.parseObject(response);
String errorMsg = all.getString("error_msg");
if (!"成功".equals(errorMsg)) {
throw new CustomizeRunTimeException("阿里云简历识别失败");
}
JSONObject parseObject = all.getJSONObject("data").getJSONObject("cv_parse");
Map<String, Object> saveResume = parseAlyJsonToMap(parseObject);
returnMap.put("data", saveResume);
}
/**
* 千里聆解析字段内容处理
*
* @param obj
* @return
*/
private Map<String, Object> parseAlyJsonToMap(JSONObject obj) {
JSONObject basicInfo = obj.getJSONObject("basic_info");
JSONObject contact = obj.getJSONObject("contact");
JSONObject jobObjective = obj.getJSONObject("job_objective");
Map<String, Object> dataMap = new HashMap<>(16);
// 姓名
String xm = basicInfo.getString("name");
dataMap.put("xm", xm);
// 电子邮箱
String dzyx = contact.getString("email");
dataMap.put("dzyx", dzyx);
// 手机号码
String sjhm = contact.getString("mobile");
dataMap.put("sjhm", sjhm);
// 年龄
String nl = basicInfo.getString("age");
dataMap.put("nl", nl);
// 出生日期
String csrq = basicInfo.getString("birthday");
dataMap.put("csrq", parseDateObject(csrq));
// 民族
String mz = basicInfo.getString("nation");
dataMap.put("mz", mz);
// 性别
String xb = "".equals(basicInfo.getString("gender")) ? "1" : "0";
dataMap.put("xb", xb);
// 体重KG
// 身高CM
// 籍贯
dataMap.put("jg", basicInfo.getString("native_place"));
// 婚姻状况
// 当前所在地
dataMap.put("jzd", basicInfo.getString("extract_location"));
// 政治面貌
// 在职状态
// 工作经验
dataMap.put("gzjy", basicInfo.getString("work_years_original"));
// 最高学位
// 最高学历
dataMap.put("zgxl", getBrowserArray(basicInfo.getString("highest_degree"), this::getEducationLevelArray));
// 毕业院校
String byyx = basicInfo.getString("graduate_school");
dataMap.put("byyx", byyx);
// 期望税前月薪K
String qwxz = jobObjective.getString("expect_salary_upper");
dataMap.put("qwxz", qwxz);
// 现税前月薪K
// 自我评价
String zwpj = obj.getString("self_evaluate");
dataMap.put("zwpj", zwpj);
// 明细表数据
// 教育经历
JSONArray jyjl = obj.getJSONArray("educations");
List<Map<String, 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 startTime = o.getString("start_time");
String endTime = o.getString("end_time");
String studyDate = startTime + "-" + endTime;
Map<String, Object> studyMap = new HashMap<>(getDateRange(studyDate, true));
studyMap.put("xxmc", o.getString("school"));
studyMap.put("xl", getBrowserArray(o.getString("degree"), this::getEducationLevelArray));
studyMap.put("zy", o.getString("major"));
studyList.add(studyMap);
}
}
dataMap.put("jyjl", studyList);
// 工作经历
JSONArray gzjl = obj.getJSONArray("occupations");
List<Map<String, String>> workList = new ArrayList<>();
if (null != gzjl && gzjl.size() > 0) {
for (int i = 0; i < gzjl.size(); i++) {
JSONObject o = (JSONObject) gzjl.get(i);
String startTime = o.getString("start_time");
String endTime = o.getString("end_time");
String workDate = startTime + "-" + endTime;
Map<String, String> workMap = new HashMap<>(getDateRange(workDate, false));
workMap.put("gsmc", o.getString("company"));
workMap.put("gw", o.getString("title"));
workMap.put("gzzz", o.getString("desc"));
workList.add(workMap);
}
}
dataMap.put("gzjl", workList);
return dataMap;
}
/**
* 千里聆解析字段内容处理
*
* @param obj
* @return
*/
private Map<String, Object> parseQllJsonToMap(JSONObject obj) {
Map<String, Object> dataMap = new HashMap<>(16);
// 姓名
String xm = parseArray(obj.getJSONArray("姓名"));
dataMap.put("xm", xm);
// 电子邮箱
String dzyx = parseArray(obj.getJSONArray("电子邮箱"));
dataMap.put("dzyx", dzyx);
// 手机号码
String sjhm = parseArray(obj.getJSONArray("手机号"));
dataMap.put("sjhm", sjhm);
// 年龄
String nl = parseArray(obj.getJSONArray("年龄"));
dataMap.put("nl", nl);
// 出生日期
String csrq = parseArray(obj.getJSONArray("出生日期"));
dataMap.put("csrq", parseDateObject(csrq));
// 民族
// 性别
String xb = "".equals(parseArray(obj.getJSONArray("性别"))) ? "0" : "1";
dataMap.put("xb", xb);
// 体重KG
// 身高CM
// 籍贯
dataMap.put("jg", parseArray(obj.getJSONArray("籍贯")));
// 婚姻状况
// 当前所在地
dataMap.put("jzd", parseArray(obj.getJSONArray("现居住地")));
// 政治面貌
// 在职状态
// 工作经验
dataMap.put("gzjy", parseArray(obj.getJSONArray("工作经验")));
// 最高学位
dataMap.put("zgxw", getBrowserArray(parseArray(obj.getJSONArray("最高学位")), this::getDegreeArray));
// 最高学历
dataMap.put("zgxl", getBrowserArray(parseArray(obj.getJSONArray("最高学历")), this::getEducationLevelArray));
// 专业
String zy = parseArray(obj.getJSONArray("专业"));
dataMap.put("zy", zy);
// 毕业院校
String byyx = parseArray(obj.getJSONArray("毕业院校"));
dataMap.put("byyx", byyx);
// 期望税前月薪K
String qwxz = parseArray(obj.getJSONArray("期望薪资"));
dataMap.put("qwxz", qwxz);
// 现税前月薪K
// 自我评价
String zwpj = parseArray(obj.getJSONArray("个人评价"));
dataMap.put("zwpj", zwpj);
// 民族
dataMap.put("mz", parseArray(obj.getJSONArray("民族")));
// 明细表数据
// 教育经历
JSONArray jyjl = obj.getJSONArray("学业信息");
List<Map<String, 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("就读时期");
Map<String, Object> studyMap = new HashMap<>(getDateRange(studyDate, true));
studyMap.put("xxmc", o.getString("院校"));
studyMap.put("xl", getBrowserArray(o.getString("学历"), this::getEducationLevelArray));
studyMap.put("zy", o.getString("专业"));
studyList.add(studyMap);
}
}
dataMap.put("jyjl", studyList);
// 工作经历
JSONArray gzjl = obj.getJSONArray("工作信息");
List<Map<String, String>> 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("工作时间");
Map<String, String> workMap = new HashMap<>(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);
// 项目经验
// 语言能力
// 目前表单暂无字段
dataMap.put("wx", parseArray(obj.getJSONArray("微信")));
dataMap.put("qq", parseArray(obj.getJSONArray("QQ")));
dataMap.put("ah", parseArray(obj.getJSONArray("爱好")));
dataMap.put("bysj", parseArray(obj.getJSONArray("毕业时间")));
dataMap.put("zyjn", parseArray(obj.getJSONArray("专业技能")));
dataMap.put("sxjl", parseArray(obj.getJSONArray("实习经历")));
dataMap.put("yysp", parseArray(obj.getJSONArray("英语水平")));
dataMap.put("jnzs", parseArray(obj.getJSONArray("技能证书")));
dataMap.put("xyjl", parseArray(obj.getJSONArray("校园经历")));
dataMap.put("qwcsgw", parseArray(obj.getJSONArray("期望从事岗位")));
dataMap.put("qwgzdd", parseArray(obj.getJSONArray("期望工作地点")));
dataMap.put("xmjl", parseArray(obj.getJSONArray("项目经历")));
return dataMap;
}
/**
* @param resumeId
* @return
*/
private String doQllPost(int resumeId) {
BaseBean bean = new BaseBean();
String url = bean.getPropValue("resume_qianliling", "qianlilingurl");
String appId = bean.getPropValue("resume_qianliling", "appId");
String appSecret = bean.getPropValue("resume_qianliling", "appSecret");
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.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 resumeId
* @return
*/
private String doAlyPost(int resumeId) {
try {
BaseBean bean = new BaseBean();
String parseHost = bean.getPropValue("youyun", "host");
String path = bean.getPropValue("youyun", "path");
String method = bean.getPropValue("youyun", "method");
String appcode = bean.getPropValue("youyun", "appcode");
ImageFileManager manager = new ImageFileManager();
manager.getImageFileInfoById(resumeId);
Map<String, String> headers = new HashMap<>(2);
headers.put("Authorization", "APPCODE " + appcode);
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> queryMap = new HashMap<>(1);
Map<String, String> bodyMap = new HashMap<>(2);
InputStream inputStream = manager.getInputStream();
byte[] bytes = IOUtils.toByteArray(inputStream);
String base64 = Base64.encodeBase64String(bytes);
bodyMap.put("content", base64);
String fileName = manager.getImageFileName();
String suffixType = fileName.substring(fileName.lastIndexOf('.') + 1);
bodyMap.put("ext", suffixType);
HttpResponse response = HttpUtils.doPost(parseHost, path, method, headers, queryMap, bodyMap);
return EntityUtils.toString(response.getEntity());
} catch (Exception var10) {
throw new ECException("简历解析失败!详情:" + var10.getMessage());
}
}
/**
* 千里聆签名
*
* @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 ar
* @return
*/
private String parseArray(JSONArray ar) {
StringBuilder rs = new StringBuilder();
if (ar != null && ar.size() > 0) {
for (int i = 0; i < ar.size(); ++i) {
if (i == ar.size() - 1) {
rs.append(ar.get(i));
} else {
rs.append(ar.get(i)).append(",");
}
}
}
return rs.toString();
}
/**
* 转换日期对象
*
* @param value
* @return
*/
private String parseDateObject(String value) {
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
try {
sdf.parse(value);
} catch (ParseException e) {
value = "";
}
return value;
}
/**
* 获取开始时间和结束时间
*
* @param date
* @return
*/
private Map<String, String> getDateRange(String date, boolean isStudy) {
Map<String, String> dataRangeMap = new HashMap<>(2);
String[] split = date.split("-");
if (split.length == 2) {
String start = getFormatDate(split[0]);
String end = getFormatDate(split[1]);
if (start.length() == 4) {
if (isStudy) {
start += "-09-01";
} else {
start = "";
}
}
if (end.length() == 4) {
if (isStudy) {
end += "-07-01";
} else {
end = "";
}
}
dataRangeMap.put("kssj", start);
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
* @return
*/
private Map<String, String> getDegreeArray(String text) {
Map<String, String> map = new HashMap<>(2);
if (StringUtils.isNotBlank(text)) {
String id = ModeBrowserCommonInfo.getDegreeId(text);
if (StringUtils.isNotBlank(id)) {
map.put("id", id);
map.put("name", text);
}
}
return map;
}
/**
* 构建浏览按钮字段信息格式
*
* @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
* @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;
}
}