From 96f0439174fc1a763d9dabfbac7e25682e803dd5 Mon Sep 17 00:00:00 2001 From: dxfeng Date: Tue, 6 Aug 2024 10:49:37 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8E=86=E5=8D=A1=E7=89=87OCR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ResumeRecognitionController.java | 12 + .../recruit/conn/ApplicantCommonInfo.java | 85 ++- .../recruit/constant/RecruitConstant.java | 193 ++++++ .../ResumeRecognitionController.java | 56 ++ .../factory/ResumeRecognitionFactory.java | 37 ++ .../service/ResumeRecognitionService.java | 38 ++ .../impl/ResumeRecognitionServiceImpl.java | 575 ++++++++++++++++++ .../wrapper/ResumeRecognitionWrapper.java | 44 ++ 8 files changed, 1039 insertions(+), 1 deletion(-) create mode 100644 src/com/api/recruit/controller/ResumeRecognitionController.java create mode 100644 src/com/engine/recruit/constant/RecruitConstant.java create mode 100644 src/com/engine/recruit/controller/ResumeRecognitionController.java create mode 100644 src/com/engine/recruit/factory/ResumeRecognitionFactory.java create mode 100644 src/com/engine/recruit/service/ResumeRecognitionService.java create mode 100644 src/com/engine/recruit/service/impl/ResumeRecognitionServiceImpl.java create mode 100644 src/com/engine/recruit/wrapper/ResumeRecognitionWrapper.java diff --git a/src/com/api/recruit/controller/ResumeRecognitionController.java b/src/com/api/recruit/controller/ResumeRecognitionController.java new file mode 100644 index 0000000..d2fe159 --- /dev/null +++ b/src/com/api/recruit/controller/ResumeRecognitionController.java @@ -0,0 +1,12 @@ +package com.api.recruit.controller; + +import javax.ws.rs.Path; + +/** + * @author:dxfeng + * @createTime: 2023/12/12 + * @version: 1.0 + */ +@Path("/jcl/recruit/resume") +public class ResumeRecognitionController extends com.engine.recruit.controller.ResumeRecognitionController { +} diff --git a/src/com/engine/recruit/conn/ApplicantCommonInfo.java b/src/com/engine/recruit/conn/ApplicantCommonInfo.java index 319e930..6bec8ea 100644 --- a/src/com/engine/recruit/conn/ApplicantCommonInfo.java +++ b/src/com/engine/recruit/conn/ApplicantCommonInfo.java @@ -63,6 +63,41 @@ public class ApplicantCommonInfo { return cancelReason; } + /** + * 获取表单下拉框值 + * + * @param formId 表单ID + * @param fieldName 字段明湖曾 + * @param selectName 下拉框展示内容 + * @return + */ + public static String getSelectValue(String formId, String fieldName, String selectName) { + String selectValue = ""; + RecordSet rs = new RecordSet(); + rs.executeQuery("select selectvalue from workflow_selectitem where fieldid =( select id from workflow_billfield where billid = ? and fieldname = ? ) and selectname = ?", formId, fieldName, selectName); + if (rs.next()) { + selectValue = rs.getString("selectvalue"); + } + return selectValue; + } + + /** + * 获取表单下拉框展示文本 + * + * @param formId 表单ID + * @param fieldName 字段明湖曾 + * @return + */ + public static Map getSelectMap(String formId, String fieldName) { + Map selectMap = new HashMap<>(); + RecordSet rs = new RecordSet(); + rs.executeQuery("select selectvalue,selectname from workflow_selectitem where fieldid =( select id from workflow_billfield where billid = ? and fieldname = ? ) ", formId, fieldName); + while (rs.next()) { + selectMap.put(rs.getString("selectvalue"), rs.getString("selectname")); + } + return selectMap; + } + /** * 获取招聘通用浏览按钮展示内容 @@ -78,7 +113,7 @@ public class ApplicantCommonInfo { RecordSet rs = new RecordSet(); String[] split = id.split(","); for (String s : split) { - rs.executeQuery("select xxnr from uf_sjzd where id = ?", id); + rs.executeQuery("select xxnr from uf_sjzd where id = ?", s); if (rs.next()) { value.add(rs.getString("xxnr")); } @@ -238,4 +273,52 @@ public class ApplicantCommonInfo { } return null; } + + /** + * 获取ImageField + * + * @param docId + * @return + */ + public static int getImageFieldByDocId(String docId) { + RecordSet rs = new RecordSet(); + rs.executeQuery("select imagefileid from docimagefile where docid = ? ", docId); + if (rs.next()) { + return rs.getInt("imagefileid"); + } + return -1; + } + + /** + * 根据customSearchCode获取查询列表的id + * + * @param customSearchCode customSearchCode + * @return ID + */ + public static String getCustomSearchId(String customSearchCode) { + RecordSet rs = new RecordSet(); + rs.executeQuery("select id from mode_customsearch where customsearchcode = ? ", customSearchCode); + if (rs.next()) { + return rs.getString("id"); + } + return ""; + } + + /** + * 根据customSearchCode获取查询列表的地址 + * + * @param customSearchCode customSearchCode + * @param linkUrl 自定义的URL + * @return 查询列表的地址 + */ + public static String getCustomSearchLink(String customSearchCode, String linkUrl) { + if (StringUtils.isNotBlank(linkUrl)) { + return linkUrl; + } + String customSearchId = getCustomSearchId(customSearchCode); + if (StringUtils.isNotBlank(customSearchId)) { + return "/spa/cube/index.html#/main/cube/search?customid=" + customSearchId; + } + return ""; + } } diff --git a/src/com/engine/recruit/constant/RecruitConstant.java b/src/com/engine/recruit/constant/RecruitConstant.java new file mode 100644 index 0000000..f602b9c --- /dev/null +++ b/src/com/engine/recruit/constant/RecruitConstant.java @@ -0,0 +1,193 @@ +package com.engine.recruit.constant; + +import com.engine.recruit.conn.ApplicantCommonInfo; +import com.engine.recruit.exception.CustomizeRunTimeException; +import org.apache.commons.lang.StringUtils; +import weaver.general.BaseBean; + +import java.net.URI; +import java.nio.charset.StandardCharsets; + +/** + * @author:dxfeng + * @createTime: 2024/01/17 + * @version: 1.0 + */ +public class RecruitConstant { + /** + * URL分隔符 + */ + private static final String URL_SPLIT = "/"; + + + public static final String HTTP_URL; + public static final String RECRUIT_MESSAGE_TYPE; + public static final String INTERVIEW_MESSAGE_TYPE; + public static final String INTERVIEW_ADD_MESSAGE_TITLE; + public static final String INTERVIEW_CANCEL_MESSAGE_TITLE; + public static final String INTERVIEW_EVALUATE_MESSAGE_TITLE; + public static final String INTERVIEW_ADJUSTMENT_MESSAGE_TITLE; + public static final String JOIN_BLACKLIST_MESSAGE_TITLE; + public static final String SCREENING_MESSAGE_TYPE; + public static final String SCREENING_MESSAGE_TITLE; + public static final String SCREENING_MESSAGE_URL; + public static final String MOBILE_APPID_COLLECT; + public static final String COLLECT_MESSAGE_LINK; + public static final String MOBILE_APPID_OFFER; + public static final String OFFER_MOBILE_URL; + public static final String APPLICANTS_RESUMES_CATEGORY; + public static final String INTERVIEW_FEEDBACK_URL; + public static final String REMIND_SEARCH_LINK; + public static final String OCR_TYPE; + public static final String OCR_URL; + public static final String APP_ID; + public static final String APP_SECRET; + + public static final String MODE_APP_NAME; + + static BaseBean baseBean = new BaseBean(); + + static { + + HTTP_URL = getRecruitPropValue("HTTP_URL"); + RECRUIT_MESSAGE_TYPE = getRecruitPropValue("RECRUIT_MESSAGE_TYPE"); + + MOBILE_APPID_COLLECT = getRecruitPropValue("MOBILE_APPID_COLLECT"); + // 信息采集移动建模地址 + COLLECT_MESSAGE_LINK = getCompleteUrl(getRecruitPropValue("COLLECT_MESSAGE_LINK")); + MOBILE_APPID_OFFER = getRecruitPropValue("MOBILE_APPID_OFFER"); + // offer反馈移动建模地址 + OFFER_MOBILE_URL = getCompleteUrl(getRecruitPropValue("OFFER_MOBILE_URL")); + APPLICANTS_RESUMES_CATEGORY = getRecruitPropValue("APPLICANTS_RESUMES_CATEGORY"); + + // 简历订阅相关 + REMIND_SEARCH_LINK = getCompleteUrl(getRecruitPropValue("REMIND_SEARCH_LINK")); + // OCR相关 + OCR_TYPE = getRecruitPropValue("OCR_TYPE"); + OCR_URL = getRecruitPropValue("OCR_URL"); + APP_ID = getRecruitPropValue("APP_ID"); + APP_SECRET = getRecruitPropValue("APP_SECRET"); + + /*处理非必填字段,为空的话,指定默认值*/ + + // 面试反馈消息提醒,消息来源 + String interviewMessageType = getRecruitPropValue("INTERVIEW_MESSAGE_TYPE"); + if (StringUtils.isBlank(interviewMessageType)) { + interviewMessageType = RECRUIT_MESSAGE_TYPE; + } + INTERVIEW_MESSAGE_TYPE = interviewMessageType; + + // 添加面试消息提醒标题 + String interviewAddMessageTitle = getRecruitPropValue("INTERVIEW_ADD_MESSAGE_TITLE"); + if (StringUtils.isBlank(interviewAddMessageTitle)) { + interviewAddMessageTitle = "面试安排提醒"; + } + INTERVIEW_ADD_MESSAGE_TITLE = interviewAddMessageTitle; + + // 取消面试消息提醒标题 + String interviewCancelMessageTitle = getRecruitPropValue("INTERVIEW_CANCEL_MESSAGE_TITLE"); + if (StringUtils.isBlank(interviewCancelMessageTitle)) { + interviewCancelMessageTitle = "取消面试提醒"; + } + INTERVIEW_CANCEL_MESSAGE_TITLE = interviewCancelMessageTitle; + + // 面试评价消息提醒标题 + String interviewEvaluateMessageTitle = getRecruitPropValue("INTERVIEW_EVALUATE_MESSAGE_TITLE"); + if (StringUtils.isBlank(interviewEvaluateMessageTitle)) { + interviewEvaluateMessageTitle = "面试评价提醒"; + } + INTERVIEW_EVALUATE_MESSAGE_TITLE = interviewEvaluateMessageTitle; + + // 调整面试消息提醒标题 + String interviewAdjustmentMessageTitle = getRecruitPropValue("INTERVIEW_ADJUSTMENT_MESSAGE_TITLE"); + if (StringUtils.isBlank(interviewAdjustmentMessageTitle)) { + interviewAdjustmentMessageTitle = "面试调整提醒"; + } + INTERVIEW_ADJUSTMENT_MESSAGE_TITLE = interviewAdjustmentMessageTitle; + + // 人才黑名单通知,消息提醒标题 + String joinBlacklistMessageTitle = getRecruitPropValue("JOIN_BLACKLIST_MESSAGE_TITLE"); + if (StringUtils.isBlank(joinBlacklistMessageTitle)) { + joinBlacklistMessageTitle = "人才黑名单通知"; + } + JOIN_BLACKLIST_MESSAGE_TITLE = joinBlacklistMessageTitle; + + // 筛选反馈消息提醒,消息来源 + String screeningMessageType = getRecruitPropValue("SCREENING_MESSAGE_TYPE"); + if (StringUtils.isBlank(screeningMessageType)) { + screeningMessageType = RECRUIT_MESSAGE_TYPE; + } + SCREENING_MESSAGE_TYPE = screeningMessageType; + + // 筛选反馈,消息提醒标题 + String screeningMessageTitle = getRecruitPropValue("SCREENING_MESSAGE_TITLE"); + if (StringUtils.isBlank(screeningMessageTitle)) { + screeningMessageTitle = "简历筛选"; + } + SCREENING_MESSAGE_TITLE = screeningMessageTitle; + + // 筛选反馈,消息提醒链接地址 + String screeningMessageUrl = getRecruitPropValue("SCREENING_MESSAGE_URL"); + if (StringUtils.isBlank(screeningMessageUrl)) { + // 根据customSearchCode,获取默认的查询列表 + screeningMessageUrl = "/wui/index.html#/main/cube/search?customid=" + ApplicantCommonInfo.getCustomSearchId("b746e3cf80454289a3ade2b17147e297"); + } + SCREENING_MESSAGE_URL = screeningMessageUrl; + + // 面试反馈地址 + String interviewFeedbackUrl = getRecruitPropValue("INTERVIEW_FEEDBACK_URL"); + if (StringUtils.isBlank(interviewFeedbackUrl)) { + interviewFeedbackUrl = "/spa/custom/static/index.html#/main/cs/app/9277c228302347dc88a958b69ee96234_Interview"; + } + INTERVIEW_FEEDBACK_URL = getCompleteUrl(interviewFeedbackUrl); + + String modeAppName = getRecruitPropValue("MODE_APP_NAME"); + if (StringUtils.isBlank(modeAppName)) { + modeAppName = "JCL_招聘管理"; + } + MODE_APP_NAME = modeAppName; + } + + /** + * 读取jclRecruit.properties配置文件的配置项 + * + * @param key + * @return + */ + public static String getRecruitPropValue(String key) { + String value = baseBean.getPropValue("jclRecruit", key); + value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + return value; + } + + + /** + * 对外发布的URL地址,处理域名 + * + * @param url + * @return + */ + public static String getCompleteUrl(String url) { + if (StringUtils.isBlank(url)) { + return ""; + } + try { + URI u = new URI(url); + // 如果URL对象的getHost()方法获取到了域名,则直接返回原始URL + if (u.getHost() != null) { + return url; + } else { + // 否则,判断URL是否以"/"开头,如果是则在它前面拼接上域名 + if (url.startsWith(URL_SPLIT)) { + return HTTP_URL + url; + } else { + // 如果URL既不是完整URL也不是以"/"开头,则在URL开头添加"/",再拼接上域名 + return HTTP_URL + URL_SPLIT + url; + } + } + } catch (Exception e) { + throw new CustomizeRunTimeException(e); + } + } + +} diff --git a/src/com/engine/recruit/controller/ResumeRecognitionController.java b/src/com/engine/recruit/controller/ResumeRecognitionController.java new file mode 100644 index 0000000..05b93df --- /dev/null +++ b/src/com/engine/recruit/controller/ResumeRecognitionController.java @@ -0,0 +1,56 @@ +package com.engine.recruit.controller; + +import com.engine.common.util.ParamUtil; +import com.engine.common.util.ServiceUtil; +import com.engine.recruit.util.ResponseResult; +import com.engine.recruit.wrapper.ResumeRecognitionWrapper; +import weaver.hrm.HrmUserVarify; +import weaver.hrm.User; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import java.util.Map; + +/** + * @author:dxfeng + * @createTime: 2023/12/12 + * @version: 1.0 + */ +public class ResumeRecognitionController { + public ResumeRecognitionWrapper getResumeRecognitionWrapper(User user) { + return ServiceUtil.getService(ResumeRecognitionWrapper.class, user); + } + + @POST + @Path("/resumeUpload") + @Produces(MediaType.APPLICATION_JSON) + public String resumeUpload(@Context HttpServletRequest request, @Context HttpServletResponse response) { + User user = HrmUserVarify.getUser(request, response); + Map param = ParamUtil.request2Map(request); + return new ResponseResult, Map>(user).run(getResumeRecognitionWrapper(user)::resumeUpload, param); + } + + @POST + @Path("/importResume") + @Produces(MediaType.APPLICATION_JSON) + public String importResume(@Context HttpServletRequest request, @Context HttpServletResponse response) { + User user = HrmUserVarify.getUser(request, response); + Map param = ParamUtil.request2Map(request); + return new ResponseResult, Map>(user).run(getResumeRecognitionWrapper(user)::importResume, param); + } + + @GET + @Path("/fetchResume") + @Produces(MediaType.APPLICATION_JSON) + public String fetchResume(@Context HttpServletRequest request, @Context HttpServletResponse response) { + User user = HrmUserVarify.getUser(request, response); + Map param = ParamUtil.request2Map(request); + return new ResponseResult, Map>(user).run(getResumeRecognitionWrapper(user)::fetchResume, param); + } +} diff --git a/src/com/engine/recruit/factory/ResumeRecognitionFactory.java b/src/com/engine/recruit/factory/ResumeRecognitionFactory.java new file mode 100644 index 0000000..d2db211 --- /dev/null +++ b/src/com/engine/recruit/factory/ResumeRecognitionFactory.java @@ -0,0 +1,37 @@ +package com.engine.recruit.factory; + +import com.engine.core.impl.Service; +import com.engine.recruit.exception.CustomizeRunTimeException; +import com.engine.recruit.service.ResumeRecognitionService; +import weaver.general.BaseBean; + +/** + * @author:dxfeng + * @createTime: 2024/01/09 + * @version: 1.0 + */ +public class ResumeRecognitionFactory { + + /** + * 根据类的全路径获取类对象 + * + * @param className 类全路径 + * @param + * @return + */ + public static Class getClass(String className) { + Class clazz; + try { + clazz = Class.forName(className); + if (!ResumeRecognitionService.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("该类未实现ResumeRecognitionService"); + } + return (Class) clazz; + } catch (ClassNotFoundException e) { + new BaseBean().writeLog(e); + throw new CustomizeRunTimeException("简历识别配置出错,未获取到解析实现类[" + className + "]"); + } + + + } +} diff --git a/src/com/engine/recruit/service/ResumeRecognitionService.java b/src/com/engine/recruit/service/ResumeRecognitionService.java new file mode 100644 index 0000000..969fa6f --- /dev/null +++ b/src/com/engine/recruit/service/ResumeRecognitionService.java @@ -0,0 +1,38 @@ +package com.engine.recruit.service; + +import java.util.Map; + +/** + * 简历识别服务类 + * + * @author:dxfeng + * @createTime: 2023/12/12 + * @version: 1.0 + */ +public interface ResumeRecognitionService { + + /** + * 简历上传、简历解析 + * + * @param param + * @return + */ + Map resumeUpload(Map param); + + /** + * 简历上传、解析 + * + * @param param + * @return + */ + Map importResume(Map param); + + + /** + * 获取简历 + * + * @param param + * @return + */ + Map fetchResume(Map param); +} diff --git a/src/com/engine/recruit/service/impl/ResumeRecognitionServiceImpl.java b/src/com/engine/recruit/service/impl/ResumeRecognitionServiceImpl.java new file mode 100644 index 0000000..9d8b69d --- /dev/null +++ b/src/com/engine/recruit/service/impl/ResumeRecognitionServiceImpl.java @@ -0,0 +1,575 @@ +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.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.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 resumeUpload(Map param) { + Map 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 importResume(Map param) { + Map returnMap = new HashMap<>(); + String resumeId = Util.null2String(param.get("resumeId")); + Map uploadDataMap = new HashMap<>(); + qllResumeUpload(Convert.toInt(resumeId), uploadDataMap, false); + Map resumeData = (Map) 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> studyList = (List>) resumeData.remove("jyjl"); + List> workList = (List>) resumeData.remove("gzjl"); + List> projectList = (List>) resumeData.remove("xmjy"); + + // 状态 + resumeData.put("zt", ApplicationStatusEnum.DISTRIBUTION.getValue()); + + List fieldList = RecruitModeUtil.getFieldList("uf_jcl_yppc"); + Set keySet = fieldList.stream().map(Formfield::getFieldname).collect(Collectors.toSet()); + + // 移除不在 keySet 中的键值对 + resumeData.entrySet().removeIf(entry -> !keySet.contains(entry.getKey())); + + + // 判断简历信息 + CheckRepeatResume instance = CheckRepeatResume.getInstance(); + Map 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 fetchResume(Map 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 dataMap = RecruitRecordSet.getSingleRecordMap(rs); + + List fieldList = RecruitModeUtil.getFieldList("uf_jcl_yppc"); + Map 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 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")); + } + JSONObject result = resultall.getJSONObject("result"); + baseBean.writeLog("千里聆OCR解析数据:" + result); + Map dataMap = parseQllJsonToMapV2(result, isCard); + returnMap.put("data", dataMap); + } + + + + /** + * 千里聆解析字段内容处理(V2) + * + * @param obj + * @return + */ + private Map parseQllJsonToMapV2(JSONObject obj, boolean isCard) { + Map 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> 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> 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> 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> 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> 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> 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> 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 studyMap = new RecruitDataMap<>(); + studyMap.putAll(getDateRange(studyDate, true)); + String schoolName = o.getString("学校名称"); + studyMap.put("xxmc", schoolName); + String educationLevel = o.getString("学历"); + List> 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> 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 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> 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 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 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 getDateRange(String date, boolean isStudy) { + RecruitDataMap 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> getBrowserArray(String text, Function> fun) { + List> list = new ArrayList<>(); + if (StringUtils.isNotBlank(text)) { + String[] split = text.split(","); + for (String s : split) { + Map apply = fun.apply(s); + list.add(apply); + } + } + + return list; + } + + /** + * 构建浏览按钮字段信息格式 + * + * @param text + * @param browserType + * @return + */ + private List> getBrowserArray(String text, String browserType) { + List> list = new ArrayList<>(); + if (StringUtils.isNotBlank(text)) { + String[] split = text.split(","); + for (String s : split) { + Map 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 getEducationLevelArray(String text) { + Map 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; + } + + +} diff --git a/src/com/engine/recruit/wrapper/ResumeRecognitionWrapper.java b/src/com/engine/recruit/wrapper/ResumeRecognitionWrapper.java new file mode 100644 index 0000000..77fe7c8 --- /dev/null +++ b/src/com/engine/recruit/wrapper/ResumeRecognitionWrapper.java @@ -0,0 +1,44 @@ +package com.engine.recruit.wrapper; + +import com.engine.common.util.ServiceUtil; +import com.engine.core.impl.Service; +import com.engine.recruit.constant.RecruitConstant; +import com.engine.recruit.factory.ResumeRecognitionFactory; +import com.engine.recruit.service.ResumeRecognitionService; +import org.apache.commons.lang3.StringUtils; +import weaver.hrm.User; + +import java.util.Map; + +/** + * @author:dxfeng + * @createTime: 2023/12/12 + * @version: 1.0 + */ +public class ResumeRecognitionWrapper extends Service { + + private ResumeRecognitionService getResumeRecognitionService(User user) { + return ServiceUtil.getService(ResumeRecognitionFactory.getClass(RecruitConstant.OCR_TYPE), user); + } + + public Map resumeUpload(Map param) { + if (StringUtils.isBlank(RecruitConstant.OCR_TYPE)) { + return null; + } + return getResumeRecognitionService(user).resumeUpload(param); + } + + public Map importResume(Map param) { + if (StringUtils.isBlank(RecruitConstant.OCR_TYPE)) { + return null; + } + return getResumeRecognitionService(user).importResume(param); + } + + public Map fetchResume(Map param) { + if (StringUtils.isBlank(RecruitConstant.OCR_TYPE)) { + return null; + } + return getResumeRecognitionService(user).fetchResume(param); + } +}