RecruitmentDemandService.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. package com.kingdee.eas.custom.recuritment.task;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.kingdee.bos.BOSException;
  5. import com.kingdee.bos.Context;
  6. import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
  7. import com.kingdee.bos.metadata.entity.SelectorItemCollection;
  8. import com.kingdee.bos.metadata.entity.SelectorItemInfo;
  9. import com.kingdee.eas.basedata.org.AdminOrgUnitFactory;
  10. import com.kingdee.eas.basedata.org.AdminOrgUnitInfo;
  11. import com.kingdee.eas.basedata.org.IAdminOrgUnit;
  12. import com.kingdee.eas.basedata.person.IPerson;
  13. import com.kingdee.eas.basedata.person.PersonFactory;
  14. import com.kingdee.eas.basedata.person.PersonInfo;
  15. import com.kingdee.eas.common.EASBizException;
  16. import com.kingdee.eas.custom.beisen.utils.BeisenApiClient;
  17. import com.kingdee.eas.custom.beisen.utils.BeisenParam;
  18. import com.kingdee.eas.custom.recuritment.bizEnum.ExecuteResultEnum;
  19. import com.kingdee.eas.hr.base.HRBillStateEnum;
  20. import com.kingdee.shr.base.syssetting.context.SHRContext;
  21. import com.kingdee.shr.recuritment.*;
  22. import com.kingdee.shr.recuritment.app.util.db.RecDBUtils;
  23. import org.apache.commons.collections.CollectionUtils;
  24. import org.apache.commons.lang3.ObjectUtils;
  25. import org.apache.commons.lang3.StringUtils;
  26. import org.apache.log4j.Logger;
  27. import java.io.IOException;
  28. import java.text.SimpleDateFormat;
  29. import java.util.*;
  30. import java.util.concurrent.ConcurrentHashMap;
  31. /**
  32. * 招聘需求推送服务(谷医堂sHR招聘需求)
  33. */
  34. public class RecruitmentDemandService {
  35. private static final Logger logger = Logger.getLogger(RecruitmentDemandService.class);
  36. /**
  37. * 是否为生产环境
  38. */
  39. private static boolean isProductionEnv = true ;
  40. // 常量定义
  41. private static final int REQUIREMENT_STATUS_IN_PROGRESS = 40;
  42. private static final int SHARE_TYPE_SUBORDINATE = 2;
  43. private static final int REQUIREMENT_TYPE_NEW = 1;
  44. private static final int REQUIREMENT_TYPE_SUPPLY = 4;
  45. // 值映射关系
  46. private static final Map<String, Integer> REQUIREMENT_TYPE_MAP = createRequirementTypeMap();
  47. private static final Map<String, Integer> CATEGORY_MAP = createCategoryMap();
  48. private static final Map<String, Integer> KIND_MAP = createKindMap();
  49. private static final Map<String, String> HEADCOUNT_TYPE_MAP = createHeadcountTypeMap();
  50. private static final Map<String, String> YES_NO_MAP = createYesNoMap();
  51. private static final Map<String, String> TRAIN_PERIOD_MAP = createTrainPeriodMap();
  52. private static final Map<String, String> FORMAL_CUSTOM_FIELDS = createFormalCustomFields();
  53. private static final Map<String, String> TEST_CUSTOM_FIELDS = createTestCustomFields();
  54. //招聘类型
  55. private static final Map<String, Integer> RECRUITMENTTYPE_MAP = createRecruitmentType();
  56. // private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
  57. // 缓存工号到用户ID的映射(避免重复查询)
  58. private static final Map<String, Integer> jobNumberToUserIdCache = new ConcurrentHashMap<>();
  59. private static final Map<String, Integer> STATUS_MAPPING = new HashMap<>();
  60. static {
  61. // 初始化映射关系 (billState/DliState, flowState) -> 需求状态值
  62. STATUS_MAPPING.put(key(3, 0), 40); // 进行中
  63. STATUS_MAPPING.put(key(3, 1), 60); // 已完成
  64. STATUS_MAPPING.put(key(3, 2), 50); // 已关闭
  65. STATUS_MAPPING.put(key(0, 3), 10); // 未提交
  66. STATUS_MAPPING.put(key(1, 0), 20); // 审批中
  67. STATUS_MAPPING.put(key(2, 0), 20); // 审批中
  68. }
  69. // 辅助方法:生成组合键
  70. private static String key(int state1, int state2) {
  71. return state1 + "|" + state2;
  72. }
  73. /**
  74. * 映射 shr 的 billState 和 flowState 到需求状态
  75. * @param billState billState值
  76. * @param flowState flowState值
  77. * @return 需求状态值,未匹配时返回null
  78. */
  79. public static Integer mapShrToStatus(int billState, int flowState) {
  80. return STATUS_MAPPING.get(key(billState, flowState));
  81. }
  82. public RecruitmentDemandService( boolean isProductionEnv) {
  83. this.isProductionEnv = isProductionEnv;
  84. }
  85. // 初始化映射关系的方法
  86. private static Map<String, Integer> createRequirementTypeMap() {
  87. Map<String, Integer> map = new HashMap<>();
  88. map.put("002", REQUIREMENT_TYPE_SUPPLY); // 新增编制 -> 缺编补充
  89. map.put("001", REQUIREMENT_TYPE_NEW); // 缺编补充 -> 新增
  90. return Collections.unmodifiableMap(map);
  91. }
  92. private static Map<String, Integer> createCategoryMap() {
  93. Map<String, Integer> map = new HashMap<>();
  94. map.put("001", 1); // 社会招聘
  95. map.put("002", 2); // 校园招聘
  96. map.put("006", 3); // 实习生招聘
  97. return Collections.unmodifiableMap(map);
  98. }
  99. private static Map<String, Integer> createKindMap() {
  100. Map<String, Integer> map = new HashMap<>();
  101. map.put("1", 1); // 全职
  102. map.put("2", 2); // 兼职
  103. map.put("0", 4); // 不限 -> 其他
  104. return Collections.unmodifiableMap(map);
  105. }
  106. private static Map<String, String> createHeadcountTypeMap() {
  107. Map<String, String> map = new HashMap<>();
  108. map.put("0", "0"); // 薪酬包预算内招聘申请
  109. map.put("1", "1"); // 编制外招聘申请
  110. map.put("2", "2"); // 薪酬包预算外招聘申请
  111. return Collections.unmodifiableMap(map);
  112. }
  113. private static Map<String, String> createYesNoMap() {
  114. Map<String, String> map = new HashMap<>();
  115. map.put("是", "是");
  116. map.put("否", "否");
  117. return Collections.unmodifiableMap(map);
  118. }
  119. private static Map<String, String> createTrainPeriodMap() {
  120. Map<String, String> map = new HashMap<>();
  121. map.put("three", "3月");
  122. map.put("six", "6月");
  123. map.put("twelve", "12月");
  124. return Collections.unmodifiableMap(map);
  125. }
  126. //招聘类型
  127. private static Map<String, Integer> createRecruitmentType() {
  128. Map<String, Integer> map = new HashMap<>();
  129. // 初始化映射关系
  130. map.put("001", 1); // 社会招聘
  131. map.put("002", 2); // 校园招聘
  132. map.put("006", 3); // 实习生招聘
  133. map.put("003", 4); // 内部推荐
  134. map.put("004", 5); // 内部竞聘
  135. map.put("005", 6); // 猎头招聘
  136. map.put("007", 7); // 外包(派遣)
  137. map.put("008", 8); // 管培生
  138. return Collections.unmodifiableMap(map);
  139. }
  140. private static Map<String, String> createFormalCustomFields() {
  141. Map<String, String> map = new HashMap<>();
  142. map.put("招聘职位", "extzhaopinzhiwei_614492_675953134");
  143. map.put("招聘业务组织", "extzhaopinyewuzuzhi_614492_625299195");
  144. map.put("招聘申请类型", "extzhaopinshenqingleixing_614492_1996095690");
  145. map.put("组织层级", "extzhaopinbumencengji_614492_28373535");
  146. map.put("是否营销类", "extshifouyingxiaolei_614492_686667835");
  147. map.put("工资待遇", "extgongzidaiyu_614492_1581268897");
  148. map.put("福利待遇", "extfulidaiyu_614492_1723263995");
  149. map.put("备注", "extbeizhu_614492_449117520");
  150. map.put("培养周期", "extpeiyangzhouqi_614492_13124793");
  151. map.put("是否需轮岗", "extshifouxulungang_614492_1020570825");
  152. map.put("轮岗岗位", "extlunganggangwei_614492_1371281235");
  153. map.put("定岗岗位", "extdingganggangwei_614492_1002839243");
  154. map.put("转正考核标准", "extzhuanzhengkaohebiaozhun_614492_1527148155");
  155. map.put("办公地点", "extbangongdidian_614492_920550595");
  156. map.put("招聘类型", "extzhaopinqudao_614492_409009478");
  157. return Collections.unmodifiableMap(map);
  158. }
  159. private static Map<String, String> createTestCustomFields() {
  160. Map<String, String> map = new HashMap<>();
  161. map.put("招聘职位", "extzhaopinzhiwei_433899_675953134");
  162. map.put("招聘业务组织", "extzhaopinyewuzuzhi_433899_625299195");
  163. map.put("招聘申请类型", "extzhaopinshenqingleixing_433899_1996095690");
  164. map.put("组织层级", "extzhaopinbumencengji_433899_28373535");
  165. map.put("是否营销类", "extshifouyingxiaolei_433899_686667835");
  166. map.put("工资待遇", "extgongzidaiyu_433899_1581268897");
  167. map.put("福利待遇", "extfulidaiyu_433899_1723263995");
  168. map.put("备注", "extbeizhu_433899_449117520");
  169. map.put("培养周期", "extpeiyangzhouqi_433899_13124793");
  170. map.put("是否需轮岗", "extshifouxulungang_433899_1020570825");
  171. map.put("轮岗岗位", "extlunganggangwei_433899_1371281235");
  172. map.put("定岗岗位", "extdingganggangwei_433899_1002839243");
  173. map.put("转正考核标准", "extzhuanzhengkaohebiaozhun_433899_1527148155");
  174. map.put("办公地点", "extbangongdidian_433899_920550595");
  175. map.put("招聘类型", "extzhaopinqudao_433899_409009478");
  176. return Collections.unmodifiableMap(map);
  177. }
  178. /**
  179. * 构建需求数据公共部分
  180. * @throws BOSException
  181. * @throws EASBizException
  182. */
  183. private JSONObject buildRequirementData(Context ctx, RecuritmentDemandInfo demandInfo) throws BOSException, EASBizException {
  184. JSONObject params = new JSONObject();
  185. // 需求类型映射
  186. RecPersonType recPersonType = demandInfo.getRecPersonType();
  187. if (recPersonType != null && StringUtils.isNotBlank(recPersonType.getValue())) {
  188. Integer mappedType = REQUIREMENT_TYPE_MAP.get(recPersonType.getValue());
  189. params.put("requirementType", mappedType != null ? mappedType : REQUIREMENT_TYPE_NEW);
  190. // //test
  191. // params.put("requirementType", 1);
  192. }
  193. // 基本字段映射
  194. params.put("name", demandInfo.getPositionName());
  195. params.put("requirementCount", demandInfo.getRecuritNumber());
  196. params.put("jobDescription", demandInfo.getResponsibilities());
  197. params.put("qualification", demandInfo.getQualification());
  198. AdminOrgUnitInfo department = demandInfo.getDepartment();
  199. if(null!= department) {
  200. IAdminOrgUnit orgIns = AdminOrgUnitFactory.getLocalInstance(ctx);
  201. department = orgIns.getAdminOrgUnitInfo(new ObjectUuidPK(department.getId().toString()));
  202. params.put("orgCode", department.getNumber());
  203. // params.put("orgName", department.getName());
  204. // //test
  205. // params.put("departmentId", 0);
  206. }
  207. // 日期字段
  208. Date applyDate = demandInfo.getApplyDate();
  209. if (applyDate != null) {
  210. SimpleDateFormat dfmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
  211. params.put("createDate", dfmt.format(applyDate));
  212. }
  213. Date arrivalDate = demandInfo.getArrivalDate();
  214. if (arrivalDate != null) {
  215. SimpleDateFormat dfmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
  216. params.put("arivalTime", dfmt.format(arrivalDate));
  217. }
  218. // 值映射字段
  219. mapValueIfPresent(CATEGORY_MAP, demandInfo.getRecuritmentType(), "category", params);
  220. mapEnumValue(KIND_MAP, demandInfo.getJobNature(), "kind", params);
  221. // 申请人转换
  222. PersonInfo proposer = demandInfo.getProposer();
  223. if (proposer != null ) {
  224. IPerson pIns = PersonFactory.getLocalInstance(ctx);
  225. proposer = pIns.getPersonInfo(new ObjectUuidPK(proposer.getId().toString()));
  226. Integer beisenUserId = convertToBeisenUserId(proposer.getNumber());
  227. //test 1038350
  228. // Integer beisenUserId = convertToBeisenUserId("1038350");
  229. if (beisenUserId != null) {
  230. params.put("createBy", beisenUserId);
  231. params.put("dutyUser", beisenUserId);
  232. }
  233. }
  234. HRBillStateEnum billState = demandInfo.getBillState();
  235. FlowStateEnum flowState = demandInfo.getFlowState();
  236. Integer rStatus = mapShrToStatus(billState.getValue(), flowState.getValue());
  237. params.put("requirementStatus", rStatus);
  238. // 处理自定义字段
  239. addCustomFields( ctx, params, demandInfo);
  240. return params;
  241. }
  242. /**
  243. * 添加自定义字段
  244. * @throws BOSException
  245. * @throws EASBizException
  246. */
  247. private void addCustomFields(Context ctx, JSONObject params, RecuritmentDemandInfo demandInfo) throws BOSException, EASBizException {
  248. // 招聘职位
  249. addCustomField(params, "招聘职位", demandInfo.getRecuritPosition().getName());
  250. // 招聘业务组织
  251. addCustomField(params, "招聘业务组织", demandInfo.getHrOrgUnit().getName());
  252. // 其他自定义字段
  253. // 招聘申请类型 (headcountType -> 自定义字段)
  254. String headcountType = Optional.ofNullable(demandInfo.get("headcountType"))
  255. .map(Object::toString) // 任何类型安全转String
  256. .orElse("");
  257. addCustomField(params, "招聘申请类型", mapHeadcountType(headcountType));
  258. addCustomField(params, "组织层级", String.valueOf(demandInfo.getDepartment().getLevel()));
  259. addCustomField(params, "是否营销类", mapYesNo(getPropertyValue(demandInfo, "isMarketing")));
  260. SalaryEnum salary = demandInfo.getSalary();
  261. if(null!= salary) {
  262. String alias = salary.getAlias();
  263. addCustomField(params, "工资待遇", alias);
  264. }
  265. addCustomField(params, "福利待遇", getBenefits(demandInfo));
  266. addCustomField(params, "备注", demandInfo.getRecRema());
  267. // 培养周期特殊处理
  268. String trainPeriod = Optional.ofNullable(getPropertyValue(demandInfo, "trainPeriod"))
  269. .map(Object::toString)
  270. .map(key -> TRAIN_PERIOD_MAP.getOrDefault(key, ""))
  271. .orElse("");
  272. addCustomField(params, "培养周期", trainPeriod);
  273. // 其他字段
  274. addCustomField(params, "是否需轮岗", mapYesNo(getPropertyValue(demandInfo, "needJobRotation")));
  275. addCustomField(params, "轮岗岗位", getPropertyAsString(demandInfo, "rotationPosition"));
  276. addCustomField(params, "定岗岗位", getPropertyAsString(demandInfo, "fixedPosition"));
  277. addCustomField(params, "转正考核标准", getPropertyAsString(demandInfo, "regularizationStandard"));
  278. addCustomField(params, "办公地点", getPropertyAsString(demandInfo, "workPlace"));
  279. //
  280. // if( null != demandInfo.get("demandCompType")) {
  281. // DemandCompTypeInfo demandCompType = (DemandCompTypeInfo) demandInfo.get("demandCompType");
  282. // addCustomField(params, "招聘类型", demandCompType.getRecTypeName() );
  283. // }
  284. DemandCompTypeCollection demandCompType = demandInfo.getDemandCompType();
  285. if(null != demandCompType && demandCompType.size() > 0) {
  286. DemandCompTypeInfo demandCompTypeInfo = demandCompType.get(0);
  287. RecType4BaseItemInfo recType = demandCompTypeInfo.getRecType();
  288. if( null != recType ) {
  289. IRecType4BaseItem retTypeIns = RecType4BaseItemFactory.getLocalInstance(ctx);
  290. recType = retTypeIns.getRecType4BaseItemInfo( new ObjectUuidPK(recType.getId().toString()));
  291. String number = recType.getNumber();
  292. Integer val = RECRUITMENTTYPE_MAP.get(number);
  293. addCustomField(params, "招聘类型", val );
  294. }
  295. }
  296. }
  297. /**
  298. * 创建招聘需求
  299. * @throws BOSException
  300. * @throws EASBizException
  301. */
  302. public String createRequirement(Context ctx, RecuritmentDemandInfo demandInfo,JSONObject requirementData) throws IOException, EASBizException, BOSException {
  303. JSONObject response = new JSONObject();
  304. try {
  305. response = BeisenApiClient.getInstance().callApi(BeisenParam.CREATE_REQUIREMENT_URL, requirementData);
  306. handleApiResponse(ctx,response,demandInfo , "");
  307. }catch (Exception e) {
  308. handleApiResponse(ctx,response,demandInfo , "");
  309. logger.error("创建招聘需求时发生错误", e);
  310. }
  311. return response.getString("data");
  312. }
  313. /**
  314. * 推送招聘需求到北森系统
  315. * @param demandInfo sHR系统的招聘需求数据
  316. * @return 北森系统返回的需求ID
  317. */
  318. public String createRecruitmentDemand(Context ctx, RecuritmentDemandInfo demandInfo) throws Exception {
  319. JSONObject params = buildRequirementData( ctx, demandInfo);
  320. // 需求状态固定为40(进行中)
  321. params.put("shareType", SHARE_TYPE_SUBORDINATE); // 共享方式固定为2(共享给下级)
  322. return createRequirement( ctx, demandInfo,params);
  323. }
  324. public void updateRequirement(Context ctx, String requirementId, RecuritmentDemandInfo demandInfo) throws BOSException, EASBizException {
  325. JSONObject params = buildRequirementData( ctx, demandInfo);
  326. params.put("requirementId", requirementId);
  327. // params.put("requirementStatus", REQUIREMENT_STATUS_IN_PROGRESS); // 进行中
  328. JSONObject response = new JSONObject();
  329. try {
  330. BeisenApiClient apiClient = BeisenApiClient.getInstance();
  331. response = apiClient.callPutApi(BeisenParam.UPDATE_REQUIREMENT_URL, params);
  332. handleApiResponse(ctx,response,demandInfo , requirementId);
  333. } catch (Exception e) {
  334. handleApiResponse(ctx,response,demandInfo , requirementId);
  335. logger.error("更新招聘需求时发生错误", e);
  336. }
  337. }
  338. /**
  339. * 处理API响应
  340. * @throws BOSException
  341. * @throws EASBizException
  342. */
  343. private void handleApiResponse(Context ctx, JSONObject response, RecuritmentDemandInfo demandInfo ,String requirementId) throws BOSException, EASBizException {
  344. //如果不包含code字段,则认为是错误的响应
  345. int code = 500;
  346. if (response.containsKey("code")) {
  347. code = response.getIntValue("code");
  348. }
  349. String message = "发生调用错误,请查看后台日志!";
  350. if (response.containsKey("message")) {
  351. message = response.getString("message");
  352. }
  353. SelectorItemCollection selectorCol = new SelectorItemCollection();
  354. selectorCol.add(new SelectorItemInfo("requirementId"));
  355. selectorCol.add(new SelectorItemInfo("syncBeisenResult"));
  356. selectorCol.add(new SelectorItemInfo("syncStatus"));
  357. if (code == 200) {
  358. if(!StringUtils.isEmpty(requirementId)) {
  359. demandInfo.put("requirementId", requirementId);
  360. }else {
  361. demandInfo.put("requirementId", response.getString("data"));
  362. }
  363. demandInfo.put("syncStatus", ExecuteResultEnum.SUCCESS_VALUE);
  364. demandInfo.put("syncBeisenResult", "成功: " + message);
  365. logger.info( "成功: " + message);
  366. } else {
  367. logger.error("失败: " + message + " (code: " + code + ")");
  368. demandInfo.put("syncStatus", ExecuteResultEnum.ERROR_VALUE);
  369. demandInfo.put("syncBeisenResult", "失败: " + message + " (code: " + code + ")");
  370. }
  371. IRecuritmentDemand demandIns = RecuritmentDemandFactory.getLocalInstance(ctx);
  372. demandIns.updatePartial(demandInfo, selectorCol);
  373. }
  374. /**
  375. * 获取属性值
  376. */
  377. private Object getPropertyValue(RecuritmentDemandInfo demandInfo, String property) {
  378. return demandInfo.get(property);
  379. }
  380. /**
  381. * 获取属性字符串值
  382. */
  383. private String getPropertyAsString(RecuritmentDemandInfo demandInfo, String property) {
  384. return Optional.ofNullable(getPropertyValue(demandInfo, property))
  385. .map(Object::toString)
  386. .orElse("");
  387. }
  388. /**
  389. * 映射值如果存在
  390. */
  391. private void mapValueIfPresent(Map<String, Integer> valueMap, String sourceValue, String targetKey, JSONObject params) {
  392. if (StringUtils.isNotBlank(sourceValue)) {
  393. Integer mappedValue = valueMap.get(sourceValue);
  394. if (mappedValue != null) {
  395. params.put(targetKey, mappedValue);
  396. }
  397. }
  398. }
  399. /**
  400. * 映射枚举值
  401. */
  402. private void mapEnumValue(Map<String, Integer> valueMap, JobNatureEnum sourceEnum, String targetKey, JSONObject params) {
  403. if (sourceEnum != null) {
  404. Integer mappedValue = valueMap.get(sourceEnum.getValue());
  405. if (mappedValue != null) {
  406. params.put(targetKey, mappedValue);
  407. }
  408. }
  409. }
  410. /**
  411. * 推送福利待遇
  412. */
  413. public String getBenefits(RecuritmentDemandInfo demandInfo) {
  414. String sql = "SELECT BENEFITS.FName FROM T_REC_Benefits BENEFITS " +
  415. "JOIN T_REC_RecDemandLinkBenefits LINK01 ON (BENEFITS.FID = LINK01.FBenefitsID) " +
  416. "WHERE BENEFITS.FState = 1 AND LINK01.FDemandID = ?";
  417. StringBuilder value = new StringBuilder();
  418. try {
  419. List<Object[]> datas = RecDBUtils.getQueryData(SHRContext.getInstance().getContext(), sql,
  420. new Object[]{demandInfo.getId().toString()});
  421. if (CollectionUtils.isNotEmpty(datas)) {
  422. for (Object[] objects : datas) {
  423. if (objects.length > 0) {
  424. value.append(String.valueOf(objects[0]));
  425. }
  426. }
  427. }
  428. } catch (Exception e) {
  429. logger.error("获取福利待遇异常", e);
  430. }
  431. return value.toString();
  432. }
  433. /**
  434. * 添加自定义字段
  435. */
  436. private void addCustomField(JSONObject params, String fieldName, String value) {
  437. if (StringUtils.isNotBlank(value)) {
  438. String fieldCode = getCustomFieldCode(fieldName);
  439. if (fieldCode != null) {
  440. params.put(fieldCode, value);
  441. }
  442. }
  443. }
  444. /**
  445. * 添加自定义字段_integer
  446. */
  447. private void addCustomField(JSONObject params, String fieldName, Integer value) {
  448. if (null != value) {
  449. String fieldCode = getCustomFieldCode(fieldName);
  450. if (fieldCode != null) {
  451. params.put(fieldCode, value);
  452. }
  453. }
  454. }
  455. /**
  456. * 获取自定义字段编码
  457. */
  458. private String getCustomFieldCode(String fieldName) {
  459. Map<String, String> fieldMap = isProductionEnv ? FORMAL_CUSTOM_FIELDS : TEST_CUSTOM_FIELDS;
  460. return fieldMap.get(fieldName);
  461. }
  462. /**
  463. * 映射招聘申请类型
  464. */
  465. private String mapHeadcountType(String headcountType) {
  466. return StringUtils.isNotBlank(headcountType) ?
  467. HEADCOUNT_TYPE_MAP.getOrDefault(headcountType, headcountType) :
  468. null;
  469. }
  470. /**
  471. * 映射是否类型
  472. */
  473. private String mapYesNo(Object value) {
  474. if (ObjectUtils.isNotEmpty(value)) {
  475. String strValue = value.toString();
  476. return YES_NO_MAP.getOrDefault(strValue, strValue);
  477. }
  478. return YES_NO_MAP.get("否");
  479. }
  480. // /**
  481. // * 将sHR用户标识转换为北森用户ID
  482. // */
  483. // private Integer convertToBeisenUserId(String shrUserIdentifier) {
  484. // // 实际实现应根据业务逻辑查询映射关系
  485. // return 407802551; // 示例ID
  486. // }
  487. /**
  488. * 批量获取北森用户ID
  489. * @param jobNumbers 员工工号集合
  490. * @return 工号到用户ID的映射
  491. * @throws IOException
  492. */
  493. public Map<String, Integer> getUserIdsByJobNumbers(Set<String> jobNumbers) throws IOException {
  494. // 检查输入
  495. if (jobNumbers == null || jobNumbers.isEmpty()) {
  496. return Collections.emptyMap();
  497. }
  498. // 分离已缓存和未缓存的工号
  499. Map<String, Integer> result = new HashMap<>();
  500. List<String> needQuery = new ArrayList<>();
  501. for (String jobNumber : jobNumbers) {
  502. if (jobNumberToUserIdCache.containsKey(jobNumber)) {
  503. result.put(jobNumber, jobNumberToUserIdCache.get(jobNumber));
  504. } else {
  505. needQuery.add(jobNumber);
  506. }
  507. }
  508. // 分批查询(每次最多30个)
  509. int batchSize = 30;
  510. for (int i = 0; i < needQuery.size(); i += batchSize) {
  511. int end = Math.min(i + batchSize, needQuery.size());
  512. List<String> batch = needQuery.subList(i, end);
  513. JSONObject requestData = new JSONObject();
  514. requestData.put("jobNumbers", batch);
  515. JSONObject response = BeisenApiClient.getInstance().callApi(
  516. BeisenParam.GET_USERID_BY_JOBNUMBER_URL, requestData);
  517. // 处理响应
  518. if ("200".equals(response.getString("code"))) {
  519. JSONArray data = response.getJSONArray("data");
  520. if (data != null) {
  521. for (int j = 0; j < data.size(); j++) {
  522. JSONObject item = data.getJSONObject(j);
  523. String jobNum = item.getString("jobNumber");
  524. Integer userId = item.getInteger("userId");
  525. // 只缓存有效结果
  526. if (jobNum != null && userId != null && userId > 0) {
  527. jobNumberToUserIdCache.put(jobNum, userId);
  528. result.put(jobNum, userId);
  529. }
  530. }
  531. }
  532. } else {
  533. logger.error("批量获取用户ID失败: " + response.getString("message"));
  534. }
  535. }
  536. return result;
  537. }
  538. /**
  539. * 将sHR用户标识转换为北森用户ID
  540. */
  541. private Integer convertToBeisenUserId(String jobNumber) {
  542. if (StringUtils.isBlank(jobNumber)) {
  543. return null;
  544. }
  545. // 先尝试从缓存获取
  546. if (jobNumberToUserIdCache.containsKey(jobNumber)) {
  547. return jobNumberToUserIdCache.get(jobNumber);
  548. }
  549. // 批量查询(即使只有一个也走批量接口)
  550. try {
  551. Set<String> jobNumbers = Collections.singleton(jobNumber);
  552. Map<String, Integer> mapping = getUserIdsByJobNumbers(jobNumbers);
  553. return mapping.get(jobNumber);
  554. } catch (IOException e) {
  555. logger.error("转换工号到北森用户ID时发生错误,工号: " + jobNumber, e);
  556. return null;
  557. }
  558. }
  559. }