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.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"); // 状态 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); 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); baseBean.writeLog("千里聆相应数据:" + all); 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"); baseBean.writeLog("千里聆OCR解析数据:" + result); Map dataMap = parseQllJsonToMap(result, isCard); returnMap.put("data", dataMap); } /** * 千里聆解析字段内容处理 * * @param obj * @return */ private Map parseQllJsonToMap(JSONObject obj, boolean isCard) { Map 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("性别"))) ? "1" : "0"; dataMap.put("xb", xb); // 体重(KG) // 身高(CM) // 籍贯 //dataMap.put("jg", parseArray(obj.getJSONArray("籍贯"))); // 婚姻状况 // 当前所在地 //dataMap.put("jzd", parseArray(obj.getJSONArray("现居住地"))); // 政治面貌 // 在职状态 // 工作经验 List> gzjy = getBrowserArray(parseArray(obj.getJSONArray("工作经验")), this::getWorkExperienceArray); 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(parseArray(obj.getJSONArray("最高学位")), this::getDegreeArray); if (CollectionUtils.isNotEmpty(zgxw)) { dataMap.put("zgxw", zgxw); if (!isCard) { dataMap.put("zgxw", zgxw.stream().map(item -> item.get("id")).collect(Collectors.joining(","))); } } // 最高学历 List> zgxl = getBrowserArray(parseArray(obj.getJSONArray("最高学历")), 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 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> 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)); studyMap.put("xxmc", o.getString("院校")); List> xl = getBrowserArray(o.getString("学历"), this::getEducationLevelArray); studyMap.put("xl", xl); if (!isCard && CollectionUtils.isNotEmpty(xl)) { studyMap.put("xl", xl.stream().map(item -> item.get("id")).collect(Collectors.joining(","))); } studyMap.put("zy", o.getString("专业")); 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); // 项目经验 // 语言能力 // 目前表单暂无字段 //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) { 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.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 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 StringUtils.isBlank(rs) ? null : rs.toString(); } /** * 转换日期对象 * * @param value * @return */ private String parseDateObject(String value) { if (StringUtils.isBlank(value)) { return null; } 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<>(); 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 getDegreeArray(String text) { Map 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 * @return */ private Map getWorkExperienceArray(String text) { Map map = new HashMap<>(2); if (StringUtils.isNotBlank(text)) { String id = ModeBrowserCommonInfo.getWorkExperience(text); if (StringUtils.isNotBlank(id)) { map.put("id", id); map.put("name", text); } } return map; } /** * 构建浏览按钮字段信息格式 * * @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 * @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; } }