GetEmpInfoOsfService.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package com.kingdee.eas.custom.szpgm.osf.service;
  2. import com.kingdee.bos.BOSException;
  3. import com.kingdee.bos.Context;
  4. import com.kingdee.bos.bsf.service.app.IHRMsfService;
  5. import com.kingdee.eas.common.EASBizException;
  6. import com.kingdee.eas.util.app.DbUtil;
  7. import com.kingdee.jdbc.rowset.IRowSet;
  8. import org.apache.commons.lang3.StringUtils;
  9. import java.text.SimpleDateFormat;
  10. import java.util.*;
  11. /**
  12. * 员工信息查询OSF服务
  13. * <p>
  14. * 实现 {@link IHRMsfService} 接口,提供员工综合信息的分页查询功能。
  15. * 支持按员工名称(左模糊)、员工编号(精确)、最后修改时间进行过滤筛选。
  16. * </p>
  17. *
  18. * <h3>关联数据表</h3>
  19. * <ul>
  20. * <li><b>t_bd_person</b>(pe)—— 员工基础信息表,主表</li>
  21. * <li><b>T_HR_BDEmployeeType</b>(ty)—— 用工关系类型表</li>
  22. * <li><b>T_HR_PERSONCONTACTMETHOD</b>(pm)—— 员工联系方式表</li>
  23. * <li><b>t_hr_personposition</b>(ppos)—— 员工岗位关联表</li>
  24. * <li><b>t_org_position</b>(pos)—— 岗位表,取主岗位信息</li>
  25. * <li><b>t_org_admin</b>(org)—— 行政组织表</li>
  26. * <li><b>T_HR_EMPPOSTEXPERIENCEHIS</b>(phis)—— 岗位任职历史表,通过上级岗位取当前上级</li>
  27. * <li><b>t_bd_person</b>(ppe)—— 员工基础信息表(二次关联,取上级员工信息)</li>
  28. * <li><b>t_pm_user</b>(puser)—— 用户账号表</li>
  29. * </ul>
  30. *
  31. * <h3>入参说明</h3>
  32. * <ul>
  33. * <li><b>modifyDate</b>(String,可选)—— 最后修改日期(格式yyyy-MM-dd),筛选该日期之后修改的记录</li>
  34. * <li><b>name</b>(String,可选)—— 员工名称,左模糊匹配</li>
  35. * <li><b>code</b>(String,可选)—— 员工编号,精确匹配</li>
  36. * <li><b>page</b>(int/String,可选,默认1)—— 页码</li>
  37. * <li><b>size</b>(int/String,可选,默认100,上限1000)—— 每页条数</li>
  38. * </ul>
  39. *
  40. * <h3>返回结果</h3>
  41. * <pre>{@code
  42. * {
  43. * "msg": "成功",
  44. * "code": "200",
  45. * "page": 1,
  46. * "size": 100,
  47. * "data": [{
  48. * "id": "员工FID",
  49. * "code": "员工编码",
  50. * "name": "员工名称",
  51. * "idcardno": "身份证号",
  52. * "tyname": "用工关系类型",
  53. * "inservice": "是否在职(1或4为在职)",
  54. * "gender": "性别(1:男,2:女)",
  55. * "globalroaming": "国际区号",
  56. * "ncell": "手机号码",
  57. * "joindate": "入司日期",
  58. * "orgCode": "所属组织编码",
  59. * "posCode": "主岗位编码",
  60. * "leader": ["上级员工编码列表"],
  61. * "userAccount": "登录账号",
  62. * "officephone": "办公电话",
  63. * "passportno": "护照号码",
  64. * "disporder": "顺序码",
  65. * "email": "邮箱"
  66. * }]
  67. * }
  68. * }</pre>
  69. *
  70. * @author szpgm
  71. * @since 2026/07/02
  72. */
  73. public class GetEmpInfoOsfService implements IHRMsfService {
  74. /**
  75. * 处理员工信息查询请求
  76. *
  77. * @param context EAS上下文对象,用于执行数据库查询
  78. * @param map 入参Map,包含过滤条件和分页参数
  79. * @return Map 包含查询结果、状态码和分页信息
  80. * @throws EASBizException 业务异常
  81. * @throws BOSException 平台异常
  82. */
  83. @Override
  84. public Object process(Context context, Map map) throws EASBizException, BOSException {
  85. // 返回结果容器
  86. Map<String, Object> result = new HashMap<String, Object>();
  87. List<Map<String, Object>> list = new ArrayList<>();
  88. // ==================== 入参解析 ====================
  89. // 最后修改日期(格式:yyyy-MM-dd)
  90. String modifyDate = (String) map.get("modifyDate");
  91. // 员工名称(左模糊匹配)
  92. String name = (String) map.get("name");
  93. // 员工编号(精确匹配)
  94. String code = (String) map.get("code");
  95. // 每页条数(默认100,上限1000)
  96. int maxSize = 1000;
  97. int size = 100;
  98. if (map.get("size") != null) {
  99. if (map.get("size") instanceof Integer) {
  100. size = (int) map.get("size");
  101. } else if (map.get("size") instanceof String) {
  102. size = Integer.parseInt((String) map.get("size"));
  103. }
  104. size = Math.min(maxSize, size);
  105. }
  106. // 页码(默认1)
  107. int page = 1;
  108. if (map.get("page") != null) {
  109. if (map.get("page") instanceof Integer) {
  110. page = (int) map.get("page");
  111. } else if (map.get("page") instanceof String) {
  112. page = Integer.parseInt((String) map.get("page"));
  113. }
  114. }
  115. // ==================== 查询数据 ====================
  116. try {
  117. // 初始化成功响应
  118. result.put("msg", "成功");
  119. result.put("code", "200");
  120. result.put("page", page);
  121. result.put("size", size);
  122. result.put("data", list);
  123. // 构建基础SQL,动态追加过滤条件
  124. StringBuilder sql = getSql();
  125. // SQL参数列表(与?占位符一一对应)
  126. List<Object> paramArr = new ArrayList<>();
  127. // 修改日期过滤:员工基础信息或联系方式的最后修改时间 >= 指定日期
  128. if (StringUtils.isNotBlank(modifyDate)) {
  129. sql.append(" and (pe.flastupdatetime >= ? or pm.flastupdatetime >= ?) ");
  130. paramArr.add(modifyDate);
  131. paramArr.add(modifyDate);
  132. }
  133. // 员工名称左模糊匹配
  134. if (StringUtils.isNotBlank(name)) {
  135. sql.append(" and pe.fname_l2 like ? ");
  136. paramArr.add(name + "%");
  137. }
  138. // 员工编号精确匹配
  139. if (StringUtils.isNotBlank(code)) {
  140. sql.append(" and pe.fnumber = ? ");
  141. paramArr.add(code);
  142. }
  143. // 分页:LIMIT 返回条数 OFFSET 跳过条数
  144. sql.append(" order by pe.fid desc LIMIT ? OFFSET ? ");
  145. paramArr.add(size);
  146. paramArr.add(((page - 1) * size));
  147. // 执行SQL查询
  148. IRowSet rs = DbUtil.executeQuery(context, sql.toString(), paramArr.toArray());
  149. // 以员工ID为key的Map,用于合并同一员工的多条记录(一个员工可能有多个上级)
  150. Map<String, Map<String, Object>> maps = new HashMap<>();
  151. // 日期格式化器(yyyy-MM-dd)
  152. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
  153. // 遍历结果集,将同一员工的多行数据合并为一条记录
  154. while (rs.next()) {
  155. String id = rs.getString("id");
  156. Map<String, Object> param = new HashMap<>();
  157. // Map<String, Object> param = maps.get(id);
  158. // // 首次遇到该员工时,初始化记录并创建上级编码列表
  159. // if (null == param) {
  160. // param = new HashMap<>();
  161. // List<String> leaderIds = new ArrayList<>();
  162. // param.put("leader", leaderIds);
  163. // maps.put(id, param);
  164. // }
  165. // // 收集该员工的所有上级编码(多行数据的共同字段会覆盖,但值相同)
  166. // List<String> leader = (List<String>) param.get("leader");
  167. // leader.add(rs.getString("leader"));
  168. // ---- 员工基本属性 ----
  169. // 员工ID
  170. param.put("id", id);
  171. String leaderStr = rs.getString("leader");
  172. List<String> leaders = new ArrayList<>();
  173. if(StringUtils.isNotBlank(leaderStr)){
  174. leaders.addAll(Arrays.asList(leaderStr.split(",")));
  175. }
  176. param.put("leader", leaders);
  177. // 员工编码
  178. param.put("code", rs.getString("code"));
  179. // 员工名称
  180. param.put("name", rs.getString("name"));
  181. // 身份证号码
  182. param.put("idcardno", rs.getString("idcardno"));
  183. // 用工关系类型名称
  184. param.put("tyname", rs.getString("tyname"));
  185. // 是否在职(1或4表示在职,其他值表示不在职)
  186. param.put("inservice", rs.getString("inservice"));
  187. // 性别(1:男,2:女)
  188. param.put("gender", rs.getString("gender"));
  189. // 国际区号
  190. param.put("globalroaming", rs.getString("globalroaming"));
  191. // 手机号码
  192. param.put("ncell", rs.getString("ncell"));
  193. // ---- 入司日期(Date -> String 格式化) ----
  194. Date joindate = rs.getDate("joindate");
  195. param.put("joindate", (null != joindate ? sdf.format(joindate) : null));
  196. // ---- 组织与岗位 ----
  197. // 所属组织编码
  198. param.put("orgCode", rs.getString("orgCode"));
  199. // 主岗位编码
  200. param.put("posCode", rs.getString("posCode"));
  201. //顺序码
  202. param.put("disporder", rs.getString("disporder"));
  203. // ---- 账号与联系方式 ----
  204. // 员工登录账号
  205. param.put("userAccount", rs.getString("userAccount"));
  206. // 办公电话
  207. param.put("officephone", rs.getString("officephone"));
  208. // 护照号码
  209. param.put("passportno", rs.getString("passportno"));
  210. //邮箱
  211. param.put("email", rs.getString("email"));
  212. list.add(param);
  213. }
  214. // 将合并后的员工列表写入返回结果
  215. result.put("data", list);
  216. } catch (Exception e) {
  217. // 异常时返回错误信息
  218. e.printStackTrace();
  219. result.put("msg", e.getMessage());
  220. result.put("code", "500");
  221. }
  222. return result;
  223. }
  224. /**
  225. * 构建员工信息查询的基础SQL
  226. * <p>
  227. * 从员工基础信息表出发,LEFT JOIN 用工关系类型、联系方式、岗位信息、
  228. * 岗位任职历史、用户账号等关联表,获取员工的综合信息。
  229. * 其中岗位任职历史通过 {@code fpositionenddate >= '2199-12-31'} 条件
  230. * 取当前有效的上级记录。
  231. * </p>
  232. *
  233. * @return 包含完整SELECT语句的StringBuilder(以 WHERE 1=1 结尾,便于后续追加AND条件)
  234. */
  235. private static StringBuilder getSql() {
  236. StringBuilder sql = new StringBuilder();
  237. // /*dialect*/ 标记用于EAS平台识别SQL方言
  238. sql.append(" /*dialect*/ select ");
  239. // ===== SELECT 字段列表 =====
  240. sql.append(" pe.fid id, "); // 员工FID
  241. sql.append(" pe.fname_l2 \"name\", "); // 员工名称
  242. sql.append(" pe.fnumber code, "); // 员工编码
  243. sql.append(" pe.fidcardno idcardno, "); // 身份证号
  244. sql.append(" ty.fname_l2 tyname, "); // 用工关系类型名称
  245. sql.append(" ty.finservice inservice, "); // 是否在职标识(1/4为在职)
  246. sql.append(" pe.fgender gender, "); // 性别(1:男,2:女)
  247. sql.append(" pm.fglobalroaming globalroaming, "); // 国际区号
  248. sql.append(" pm.fncell ncell, "); // 手机号码
  249. sql.append(" ppos.fjoindate joindate, "); // 入司日期
  250. sql.append(" org.fnumber orgCode, "); // 所属组织编码
  251. sql.append(" pos.fnumber posCode, "); // 主岗位编码
  252. // 使用STRING_AGG将同一上级岗位下的多位领导编码合并为逗号分隔字段(Kingbase/PostgreSQL语法)
  253. sql.append(" (SELECT STRING_AGG(ppe_inner.fnumber, ',' ORDER BY ppe_inner.fnumber) ");
  254. sql.append(" FROM T_HR_EMPPOSTEXPERIENCEHIS phis_inner ");
  255. sql.append(" LEFT JOIN t_bd_person ppe_inner ON ppe_inner.fid = phis_inner.fpersonid ");
  256. sql.append(" WHERE phis_inner.fpositionid = pos.fparentid ");
  257. sql.append(" AND phis_inner.fpositionenddate >= '2199-12-31 00:00:00') leader, ");
  258. sql.append(" puser.fnumber userAccount, "); // 登录账号
  259. sql.append(" pm.fofficephone officephone, "); // 办公电话
  260. sql.append(" pe.findex disporder, ");
  261. sql.append(" pe.fpassportno passportno, "); // 护照号码
  262. sql.append(" pm.FEmail email ");
  263. // ===== FROM 子句及关联表 =====
  264. sql.append(" from ");
  265. sql.append(" t_bd_person pe "); // 员工基础信息表(主表)
  266. sql.append(" left join T_HR_BDEmployeeType ty on pe.femployeetypeid = ty.fid "); // 用工关系类型
  267. sql.append(" left join T_HR_PERSONCONTACTMETHOD pm on pm.fpersonid = pe.fid "); // 联系方式
  268. sql.append(" left join t_hr_personposition ppos on ppos.FPERSONID = pe.fid "); // 员工岗位关联
  269. sql.append(" left join t_org_position pos on pos.fid = ppos.fprimarypositionid "); // 主岗位信息
  270. sql.append(" left join t_org_admin org on org.fid = ppos.fpersondep "); // 所属行政组织
  271. //sql.append(" left join t_pm_user puser on puser.FPERSONID = pe.fid "); // 用户登录账号
  272. sql.append(" left join (SELECT * ");
  273. sql.append(" FROM ( ");
  274. sql.append(" SELECT ");
  275. sql.append(" *, ");
  276. sql.append(" ROW_NUMBER() OVER ( ");
  277. sql.append(" PARTITION BY FPERSONID ");
  278. sql.append(" ORDER BY feffectivedate DESC, fid DESC ");
  279. sql.append(" ) AS rn ");
  280. sql.append(" FROM t_pm_user ");
  281. sql.append(" ) t ");
  282. sql.append(" WHERE rn = 1 ) puser on puser.FPERSONID = pe.fid ");
  283. // WHERE 1=1 便于后续动态追加 AND 条件
  284. sql.append(" where 1=1 ");
  285. return sql;
  286. }
  287. }