Jelajahi Sumber

接口开发

zqy 2 minggu lalu
induk
melakukan
e4c49d0eb2

+ 71 - 53
doc/接口开发.md

@@ -88,55 +88,54 @@
 
 
 ### 三.人员接口
-参数:
+服务名:getEmpInfoOsfService
 
-| 字段名 | 类型 | 必填 | 说明 | 示例 |
-| :--- | :--- | :--- | :--- | :--- |
-| modifyTime | String | 否 | 修改时间 | yyyy-MM-dd |
+参数:
 
+| 字段名 | 类型 | 必填 | 说明                        | 示例 |
+| :--- | :--- | :--- |:--------------------------| :--- |
+| modifyDate | String | 否 | 修改日期(范围过滤:员工或联系方式 >= 该日期) | yyyy-MM-dd |
+| name | String | 否 | 员工名称(精确匹配)                 | 张三 |
+| code | String | 否 | 员工编号(精确匹配)                | 010056 |
+| size | int | 否 | 每页条数(默认100,最大1000)        | 100 |
+| page | int | 否 | 页码(默认1)                   | 1 |
 
 字段:
 
-| 描述 | 接口字段 | 类型 | 示例 |
+| 描述 | 接口字段 | 类型 | 说明 |
 | --- | --- | --- | --- |
-| <font style="color:#FF0000;">姓名</font> | <font style="color:#FF0000;">username</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">邮箱</font> | <font style="color:#FF0000;">email</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">上级</font> | <font style="color:#FF0000;">superior</font> | Array[String] | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">手机</font> | <font style="color:#FF0000;">mobile</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">性别</font> | <font style="color:#FF0000;">sex</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">工号</font> | <font style="color:#FF0000;">job_num</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">所属组织</font> | <font style="color:#FF0000;">department</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">岗位</font> | <font style="color:#FF0000;">position</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">状态</font> | <font style="color:#FF0000;">personnel_status</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">登录名</font> | <font style="color:#FF0000;">loginid</font> | string | <font style="color:#FF0000;"></font> |
-| <font style="color:#FF0000;">办公电话</font> | <font style="color:#FF0000;">telephone</font> | string | <font style="color:#FF0000;"></font> |
-| 其他上级 | other_superior | Array[String] |  |
-| 别名 | alias | string |  |
-| 入职时间 | hiredate | string |  |
-| 职级 | grade | string |  |
-| 办公地点 | location | string |  |
-| 身份证 | id_no | string |  |
-| 出生日期 | birthday | string |  |
-| 年龄 | age | string |  |
-| 毕业日期 | graduate_date | string |  |
-| 毕业院校 | graduate_school | string |  |
-| 学位 | degree | string |  |
-| 学历学历 | education | string |  |
-| 婚姻状况 | marital_status | string |  |
-| 籍贯 | native_place | string |  |
-| 民族 | nation | string |  |
-| 户口类型 | household_type | string |  |
-| 政治面貌 | politics_status | string |  |
-| 现居住地 | residence_place | string |  |
-| 家庭联系方式 | family_contact | string |  |
-| 首次参加工作日期 | first_work_date | string |  |
-| 个人工龄(年) | work_year | string |  |
-| 子女状态 | child_status | string |  |
-| 仅人员新增有效;不允许修改。(该字段仅可以传递 手机号码或者邮箱) | account | string |  |
-| 密码 | password | string |  |
-| 安全级别 | sec_level | string |  |
-| 密级 | classification | string |  |
-| 第三方主键 | outkey | string |  |
+| 员工ID | id | String | 区分大小写的字符串uuid |
+| 员工编码 | code | String |  |
+| 员工名称 | name | String |  |
+| 身份证号码 | idcardno | String |  |
+| 用工关系类型 | tyname | String |  |
+| 是否在职 | inservice | String | 1或4表示在职,其他值表示不在职 |
+| 性别 | gender | String | 1:男,2:女 |
+| 国际区号 | globalroaming | String |  |
+| 手机号码 | ncell | String |  |
+| 入司日期 | joindate | String | 格式:yyyy-MM-dd |
+| 所属组织ID | orgid | String | 区分大小写的字符串uuid |
+| 岗位ID | posid | String | 区分大小写的字符串uuid |
+| 上级ID列表 | leader | Array[String] | 一个员工可能有多个上级 |
+| 登录账号 | userAccount | String |  |
+| 办公电话 | officephone | String |  |
+| 护照号码 | passportno | String |  |
+
+返回说明:
+
+| 字段名 | 类型 | 说明 |
+| :--- | :--- | :--- |
+| code | String | 状态码(200:成功, 500:异常) |
+| msg | String | 提示信息 |
+| page | int | 当前页码 |
+| size | int | 每页条数 |
+| data | Array | 人员列表,结构见"字段"表 |
+
+使用sdk调用示例(Java)
+
+| OSFWSClient osfwsClient = new OSFWSClient();<br/>String shrAddr = "http://192.168.1.250:6888/shr";<br/>Map<String, Object> map = new HashMap();<br/>map.put("modifyDate", "2025-01-01");<br/>map.put("name", "张三");<br/>map.put("page", 1);<br/>map.put("size", 100);<br/>String result = osfwsClient.proceedOSF(shrAddr, "getEmpInfoOsfService", map);<br/>System.out.println(result); |
+|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+
 
 
 ### 四.分管领导接口
@@ -190,24 +189,43 @@
 
 
 ### 五:人员兼职接口
+服务名:getPluralityInfoOsfService
+
 参数:
 
 | 字段名 | 类型 | 必填 | 说明 | 示例 |
 | :--- | :--- | :--- | :--- | :--- |
-| modifyTime | String | 否 | 修改时间 | yyyy-MM-dd |
-
+| modifyDate | String | 否 | 修改日期(范围过滤:加入兼职单或移除兼职单 >= 该日期) | yyyy-MM-dd |
+| personid | String | 否 | 员工ID(精确匹配) | 区分大小写的字符串uuid |
+| size | int | 否 | 每页条数(默认100,最大1000) | 100 |
+| page | int | 否 | 页码(默认1) | 1 |
 
 字段:
 
-| 描述 | 接口字段 | 类型 | 示例 |
+| 描述 | 接口字段 | 类型 | 说明 |
 | --- | --- | --- | --- |
-| <font style="color:#FF0000;">主身份人员id</font> | <font style="color:#FF0000;">id</font> | String | |
-| <font style="color:#FF0000;">兼职部门或者分部</font> | <font style="color:#FF0000;">departmentId</font> | String | |
-| <font style="color:#FF0000;">兼职岗位</font> | <font style="color:#FF0000;">positionId</font> | String | |
-| <font style="color:#FF0000;">兼职上级</font> | <font style="color:#FF0000;">superior</font> | Array[String] | |
-| <font style="color:#FF0000;">兼职开始时间</font> | <font style="color:#FF0000;">start_date</font> | String | |
-| <font style="color:#FF0000;">兼职结束时间</font> | <font style="color:#FF0000;">end_date</font> | String | |
-| 备注 | remark | String | |
+| 员工ID | personid | String | 区分大小写的字符串uuid |
+| 兼职开始日期 | startDate | String | 格式:yyyy-MM-dd |
+| 兼职结束日期 | endDate | String | 格式:yyyy-MM-dd,未结束则为"2199-12-31" |
+| 兼职组织ID | orgid | String | 区分大小写的字符串uuid |
+| 兼职岗位ID | posid | String | 区分大小写的字符串uuid |
+| 上级ID列表 | leader | Array[String] | 一个员工可能有多个上级 |
+| 兼职备注 | description | String | 来自加入兼职单分录 |
+
+返回说明:
+
+| 字段名 | 类型 | 说明 |
+| :--- | :--- | :--- |
+| code | String | 状态码(200:成功, 500:异常) |
+| msg | String | 提示信息 |
+| page | int | 当前页码 |
+| size | int | 每页条数 |
+| data | Array | 兼职信息列表,结构见"字段"表 |
+
+使用sdk调用示例(Java)
+
+| OSFWSClient osfwsClient = new OSFWSClient();<br/>String shrAddr = "http://192.168.1.250:6888/shr";<br/>Map<String, Object> map = new HashMap();<br/>map.put("modifyDate", "2025-01-01");<br/>map.put("personid", "区分大小写的字符串uuid");<br/>map.put("page", 1);<br/>map.put("size", 100);<br/>String result = osfwsClient.proceedOSF(shrAddr, "getPluralityInfoOsfService", map);<br/>System.out.println(result); |
+|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 
 
 ### 六.保存打卡记录接口

+ 55 - 0
js/addon/customer/web/js/shr/ats/punchCardRecordListEx.js

@@ -0,0 +1,55 @@
+//所有下级组织ID
+var allSubOrgIDs = "";
+var orgLongNum = "";
+shr.defineClass("shr.ats.PunchCardRecordListEx", shr.ats.PunchCardRecordList, {
+
+	reportUipk: "com.kingdee.eas.hr.ats.app.PunchCardRecord.list",
+	isFirstTimeLoad: true,
+	initalizeDOM: function () {
+		shr.ats.PunchCardRecordListEx.superClass.initalizeDOM.call(this);
+	},
+	syncCardByC3Action: function () {
+		var _self = this;
+
+		openLoader(1, "同步打卡中...");
+		shr.callService({
+			serviceName: "cardToShrOSFService",
+			param: { "minusMinutes": "10" },
+			async: true,
+			success: function (data) {
+				closeLoader();
+
+				shr.showInfo({
+					message: "同步打卡完成!",
+					hideAfter: 3
+				});
+
+			}
+
+		});
+	},
+});
+
+
+
+/**
+ * 从Cookie中获取Session Id,解决上传文件302错误
+ * @returns
+ */
+function getJSessionCookie() {
+	var array = document.cookie.split(";");
+	for (var i = 0; i < array.length; i++) {
+		if (array[i].indexOf("JSESSIONID") >= 0) {
+			return array[i].trim();
+		}
+	}
+	return "";
+}
+
+
+
+
+
+
+
+

+ 279 - 0
websrc/com/kingdee/eas/custom/szpgm/osf/service/GetEmpInfoOsfService.java

@@ -0,0 +1,279 @@
+package com.kingdee.eas.custom.szpgm.osf.service;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.bsf.service.app.IHRMsfService;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.util.app.DbUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+import org.apache.commons.lang3.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 员工信息查询OSF服务
+ * <p>
+ *     实现 {@link IHRMsfService} 接口,提供员工综合信息的分页查询功能。
+ *     支持按员工名称(左模糊)、员工编号(精确)、最后修改时间进行过滤筛选。
+ * </p>
+ *
+ * <h3>关联数据表</h3>
+ * <ul>
+ *     <li><b>t_bd_person</b>(pe)—— 员工基础信息表,主表</li>
+ *     <li><b>T_HR_BDEmployeeType</b>(ty)—— 用工关系类型表</li>
+ *     <li><b>T_HR_PERSONCONTACTMETHOD</b>(pm)—— 员工联系方式表</li>
+ *     <li><b>t_hr_personposition</b>(ppos)—— 员工岗位关联表</li>
+ *     <li><b>t_org_position</b>(pos)—— 组织岗位表</li>
+ *     <li><b>T_HR_EMPPOSTEXPERIENCEHIS</b>(phis)—— 岗位任职历史表,取当前上级</li>
+ *     <li><b>t_pm_user</b>(puser)—— 用户账号表</li>
+ * </ul>
+ *
+ * <h3>入参说明</h3>
+ * <ul>
+ *     <li><b>modifyDate</b>(String,可选)—— 最后修改日期(格式yyyy-MM-dd),筛选该日期之后修改的记录</li>
+ *     <li><b>name</b>(String,可选)—— 员工名称,左模糊匹配</li>
+ *     <li><b>code</b>(String,可选)—— 员工编号,精确匹配</li>
+ *     <li><b>page</b>(int/String,可选,默认1)—— 页码</li>
+ *     <li><b>size</b>(int/String,可选,默认100,上限1000)—— 每页条数</li>
+ * </ul>
+ *
+ * <h3>返回结果</h3>
+ * <pre>{@code
+ * {
+ *     "msg": "成功",
+ *     "code": "200",
+ *     "page": 1,
+ *     "size": 100,
+ *     "data": [{
+ *         "id": "员工FID",
+ *         "code": "员工编码",
+ *         "name": "员工名称",
+ *         "idcardno": "身份证号",
+ *         "tyname": "用工关系类型",
+ *         "inservice": "是否在职(1或4为在职)",
+ *         "gender": "性别(1男,2女)",
+ *         "globalroaming": "区号",
+ *         "ncell": "电话号码",
+ *         "joindate": "入司日期",
+ *         "orgid": "组织ID",
+ *         "posid": "岗位ID",
+ *         "leader": ["上级ID列表"],
+ *         "userAccount": "登录账号",
+ *         "officephone": "办公电话",
+ *         "passportno": "护照号码"
+ *     }]
+ * }
+ * }</pre>
+ *
+ * @author szpgm
+ * @since 2026/07/02
+ */
+public class GetEmpInfoOsfService  implements IHRMsfService {
+
+    /**
+     * 处理员工信息查询请求
+     *
+     * @param context EAS上下文对象,用于执行数据库查询
+     * @param map     入参Map,包含过滤条件和分页参数
+     * @return Map 包含查询结果、状态码和分页信息
+     * @throws EASBizException 业务异常
+     * @throws BOSException    平台异常
+     */
+    @Override
+    public Object process(Context context, Map map) throws EASBizException, BOSException {
+        // 返回结果容器
+        Map<String, Object> result = new HashMap<String, Object>();
+        List<Map<String, Object>> list = new ArrayList<>();
+
+        // ==================== 入参解析 ====================
+
+        // 最后修改日期(格式:yyyy-MM-dd)
+        String modifyDate = (String) map.get("modifyDate");
+        // 员工名称(左模糊匹配)
+        String name = (String) map.get("name");
+        // 员工编号(精确匹配)
+        String code = (String) map.get("code");
+
+        // 每页条数(默认100,上限1000)
+        int maxSize = 1000;
+        int size = 100;
+        if (map.get("size") != null) {
+            if (map.get("size") instanceof Integer) {
+                size = (int) map.get("size");
+            } else if (map.get("size") instanceof String) {
+                size = Integer.parseInt((String) map.get("size"));
+            }
+            size = Math.min(maxSize, size);
+        }
+
+        // 页码(默认1)
+        int page = 1;
+        if (map.get("page") != null) {
+            if (map.get("page") instanceof Integer) {
+                page = (int) map.get("page");
+            } else if (map.get("page") instanceof String) {
+                page = Integer.parseInt((String) map.get("page"));
+            }
+        }
+
+        // ==================== 查询数据 ====================
+        try {
+            // 初始化成功响应
+            result.put("msg", "成功");
+            result.put("code", "200");
+            result.put("page", page);
+            result.put("size", size);
+            result.put("data", list);
+
+            // 构建基础SQL,动态追加过滤条件
+            StringBuilder sql = getSql();
+            // SQL参数列表(与?占位符一一对应)
+            List<Object> paramArr = new ArrayList<>();
+
+            // 修改日期过滤:员工基础信息或联系方式的最后修改时间 >= 指定日期
+            if (StringUtils.isNotBlank(modifyDate)) {
+                sql.append(" and (pe.flastupdatetime >= ? or pm.flastupdatetime >= ?) ");
+                paramArr.add(modifyDate);
+                paramArr.add(modifyDate);
+            }
+
+            // 员工名称左模糊匹配
+            if (StringUtils.isNotBlank(name)) {
+                sql.append(" and pe.fname_l2 like ? ");
+                paramArr.add(name + "%");
+            }
+
+            // 员工编号精确匹配
+            if (StringUtils.isNotBlank(code)) {
+                sql.append(" and pe.fnumber = ? ");
+                paramArr.add(code);
+            }
+
+            // 分页:LIMIT 返回条数 OFFSET 跳过条数
+            sql.append(" LIMIT ? OFFSET ? ");
+            paramArr.add(size);
+            paramArr.add(((page - 1) * size));
+
+            // 执行SQL查询
+            IRowSet rs = DbUtil.executeQuery(context, sql.toString(), paramArr.toArray());
+            // 以员工ID为key的Map,用于合并同一员工的多条记录(一个员工可能有多个上级)
+            Map<String, Map<String, Object>> maps = new HashMap<>();
+            // 日期格式化器(yyyy-MM-dd)
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+            // 遍历结果集,将同一员工的多行数据合并为一条记录
+            while (rs.next()) {
+                String id = rs.getString("id");
+                Map<String, Object> param = maps.get(id);
+                // 首次遇到该员工时,初始化记录并创建上级ID列表
+                if (null == param) {
+                    param = new HashMap<>();
+                    List<String> leaderIds = new ArrayList<>();
+                    param.put("leader", leaderIds);
+                    maps.put(id, param);
+                }
+                // 收集该员工的所有上级ID(多行数据的共同字段会覆盖,但值相同)
+                List<String> leader = (List<String>) param.get("leader");
+                leader.add(rs.getString("leader"));
+
+                // ---- 员工基本属性 ----
+                // 员工ID
+                param.put("id", id);
+                // 员工编码
+                param.put("code", rs.getString("code"));
+                // 员工名称
+                param.put("name", rs.getString("name"));
+                // 身份证号码
+                param.put("idcardno", rs.getString("idcardno"));
+                // 用工关系类型名称
+                param.put("tyname", rs.getString("tyname"));
+                // 是否在职(1或4表示在职,其他值表示不在职)
+                param.put("inservice", rs.getString("inservice"));
+                // 性别(1:男,2:女)
+                param.put("gender", rs.getString("gender"));
+                // 国际区号
+                param.put("globalroaming", rs.getString("globalroaming"));
+                // 手机号码
+                param.put("ncell", rs.getString("ncell"));
+
+                // ---- 入司日期(Date -> String 格式化) ----
+                Date joindate = rs.getDate("joindate");
+                param.put("joindate", (null != joindate ? sdf.format(joindate) : null));
+
+                // ---- 组织与岗位 ----
+                // 所属组织ID
+                param.put("orgid", rs.getString("orgid"));
+                // 主岗位ID
+                param.put("posid", rs.getString("posid"));
+
+                // ---- 账号与联系方式 ----
+                // 员工登录账号
+                param.put("userAccount", rs.getString("userAccount"));
+                // 办公电话
+                param.put("officephone", rs.getString("officephone"));
+                // 护照号码
+                param.put("passportno", rs.getString("passportno"));
+            }
+            // 将合并后的员工列表写入返回结果
+            result.put("data", maps.values());
+        } catch (Exception e) {
+            // 异常时返回错误信息
+            e.printStackTrace();
+            result.put("msg", e.getMessage());
+            result.put("code", "500");
+        }
+        return result;
+    }
+
+    /**
+     * 构建员工信息查询的基础SQL
+     * <p>
+     *     从员工基础信息表出发,LEFT JOIN 用工关系类型、联系方式、岗位信息、
+     *     岗位任职历史、用户账号等关联表,获取员工的综合信息。
+     *     其中岗位任职历史通过 {@code fpositionenddate >= '2199-12-31'} 条件
+     *     取当前有效的上级记录。
+     * </p>
+     *
+     * @return 包含完整SELECT语句的StringBuilder(以 WHERE 1=1 结尾,便于后续追加AND条件)
+     */
+    private static StringBuilder getSql() {
+        StringBuilder sql = new StringBuilder();
+        // /*dialect*/ 标记用于EAS平台识别SQL方言
+        sql.append(" /*dialect*/ select ");
+
+        // ===== SELECT 字段列表 =====
+        sql.append(" pe.fid id, ");                              // 员工FID
+        sql.append(" pe.fname_l2 \"name\", ");                   // 员工名称
+        sql.append(" pe.fnumber code, ");                        // 员工编码
+        sql.append(" pe.fidcardno idcardno, ");                  // 身份证号
+        sql.append(" ty.fname_l2 tyname, ");                    // 用工关系类型名称
+        sql.append(" ty.finservice inservice, ");                // 是否在职标识
+        sql.append(" pe.fgender gender, ");                      // 性别
+        sql.append(" pm.fglobalroaming globalroaming, ");        // 国际区号
+        sql.append(" pm.fncell ncell, ");                        // 手机号码
+        sql.append(" ppos.fjoindate joindate, ");                // 入司日期
+        sql.append(" ppos.fpersondep orgid, ");                  // 所属组织ID
+        sql.append(" ppos.fprimarypositionid posid, ");          // 主岗位ID
+        sql.append(" phis.fpersonid leader, ");                  // 上级员工ID
+        sql.append(" puser.fnumber userAccount, ");              // 登录账号
+        sql.append(" pm.fofficephone officephone, ");            // 办公电话
+        sql.append(" pe.fpassportno passportno ");               // 护照号码
+
+        // ===== FROM 子句及关联表 =====
+        sql.append(" from ");
+        sql.append(" t_bd_person pe ");                                                                          // 员工基础信息表(主表)
+        sql.append(" left join T_HR_BDEmployeeType ty on pe.femployeetypeid = ty.fid ");                        // 用工关系类型
+        sql.append(" left join T_HR_PERSONCONTACTMETHOD pm on pm.fpersonid = pe.fid ");                          // 联系方式
+        sql.append(" left join t_hr_personposition ppos on ppos.FPERSONID = pe.fid ");                           // 岗位关联
+        sql.append(" left join t_org_position pos on pos.fid = ppos.fprimarypositionid ");                       // 组织岗位(用于关联上级)
+        sql.append(" left join T_HR_EMPPOSTEXPERIENCEHIS phis on phis.fpositionid = pos.fparentid ");           // 岗位任职历史
+        sql.append(" and phis.fpositionenddate >= '2199-12-31 00:00:00' ");                                      // 只取当前有效的任职记录
+        sql.append(" left join t_pm_user puser on puser.FPERSONID = pe.fid ");                                   // 用户登录账号
+
+        // WHERE 1=1 便于后续动态追加 AND 条件
+        sql.append(" where 1=1 ");
+
+        return sql;
+    }
+}

+ 255 - 0
websrc/com/kingdee/eas/custom/szpgm/osf/service/GetPluralityInfoOsfService.java

@@ -0,0 +1,255 @@
+package com.kingdee.eas.custom.szpgm.osf.service;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.bsf.service.app.IHRMsfService;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.util.app.DbUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+import org.apache.commons.lang3.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 员工兼职信息查询OSF服务
+ * <p>
+ *     实现 {@link IHRMsfService} 接口,提供员工兼职信息的分页查询功能。
+ *     支持按员工编号(精确)、最后修改时间进行过滤筛选。
+ *     同一员工存在多个上级时,会将上级ID合并到一个列表中返回。
+ * </p>
+ *
+ * <h3>关联数据表</h3>
+ * <ul>
+ *     <li><b>T_HR_PluralityAddBizBillEntry</b>(pae)—— 员工加入兼职单分录表,主表</li>
+ *     <li><b>T_HR_PluralityAddBizBill</b>(pa)—— 员工加入兼职单表,获取审批状态</li>
+ *     <li><b>T_HR_PluralityDelBizBillentry</b>(pde)—— 员工移除兼职单分录表,获取结束日期</li>
+ *     <li><b>T_HR_PluralityDelBizBill</b>(pd)—— 员工移除兼职单表,获取移除审批状态</li>
+ *     <li><b>t_org_position</b>(pos)—— 组织岗位表,用于关联上级</li>
+ *     <li><b>T_HR_EMPPOSTEXPERIENCEHIS</b>(phis)—— 岗位任职历史表,取当前上级</li>
+ * </ul>
+ *
+ * <h3>入参说明</h3>
+ * <ul>
+ *     <li><b>modifyDate</b>(String,可选)—— 最后修改日期(格式yyyy-MM-dd),筛选该日期之后修改的兼职记录</li>
+ *     <li><b>personid</b>(String,可选)—— 员工编号/ID,精确匹配</li>
+ *     <li><b>page</b>(int/String,可选,默认1)—— 页码</li>
+ *     <li><b>size</b>(int/String,可选,默认100,上限1000)—— 每页条数</li>
+ * </ul>
+ *
+ * <h3>返回结果</h3>
+ * <pre>{@code
+ * {
+ *     "msg": "成功",
+ *     "code": "200",
+ *     "page": 1,
+ *     "size": 100,
+ *     "data": [{
+ *         "personid": "员工ID",
+ *         "startDate": "兼职开始日期(yyyy-MM-dd)",
+ *         "endDate": "兼职结束日期(yyyy-MM-dd),未结束则为2199-12-31",
+ *         "orgid": "兼职组织ID",
+ *         "posid": "兼职岗位ID",
+ *         "leader": ["上级ID列表"],
+ *         "description": "兼职备注"
+ *     }]
+ * }
+ * }</pre>
+ *
+ * <h3>业务规则</h3>
+ * <ul>
+ *     <li>只查询加入兼职单审批通过的记录(pa.fbillstate=3)</li>
+ *     <li>兼职结束日期:只有移除兼职单审批通过(pd.fbillstate=3)时才取实际结束日期,否则返回"2199-12-31"表示未结束</li>
+ * </ul>
+ *
+ * @author szpgm
+ * @since 2026/07/02
+ */
+public class GetPluralityInfoOsfService implements IHRMsfService {
+
+    /**
+     * 处理员工兼职信息查询请求
+     *
+     * @param context EAS上下文对象,用于执行数据库查询
+     * @param map     入参Map,包含过滤条件和分页参数
+     * @return Map 包含查询结果、状态码和分页信息
+     * @throws EASBizException 业务异常
+     * @throws BOSException    平台异常
+     */
+    @Override
+    public Object process(Context context, Map map) throws EASBizException, BOSException {
+        // 返回结果容器
+        Map<String, Object> result = new HashMap<String, Object>();
+        List<Map<String, Object>> list = new ArrayList<>();
+
+        // ==================== 入参解析 ====================
+
+        // 最后修改日期(格式:yyyy-MM-dd),同时检查加入兼职表和移除兼职表
+        String modifyDate = (String) map.get("modifyDate");
+        // 员工ID(精确匹配)
+        String code = (String) map.get("personid");
+
+        // 每页条数(默认100,上限1000)
+        int maxSize = 1000;
+        int size = 100;
+        if (map.get("size") != null) {
+            if (map.get("size") instanceof Integer) {
+                size = (int) map.get("size");
+            } else if (map.get("size") instanceof String) {
+                size = Integer.parseInt((String) map.get("size"));
+            }
+            size = Math.min(maxSize, size);
+        }
+
+        // 页码(默认1)
+        int page = 1;
+        if (map.get("page") != null) {
+            if (map.get("page") instanceof Integer) {
+                page = (int) map.get("page");
+            } else if (map.get("page") instanceof String) {
+                page = Integer.parseInt((String) map.get("page"));
+            }
+        }
+
+        // ==================== 查询数据 ====================
+        try {
+            // 初始化成功响应
+            result.put("msg", "成功");
+            result.put("code", "200");
+            result.put("page", page);
+            result.put("size", size);
+            result.put("data", list);
+
+            // 构建基础SQL,动态追加过滤条件
+            StringBuilder sql = getSql();
+            // SQL参数列表(与?占位符一一对应)
+            List<Object> paramArr = new ArrayList<>();
+
+            // 修改日期过滤:加入兼职单或移除兼职单的最后修改时间 >= 指定日期
+            if (StringUtils.isNotBlank(modifyDate)) {
+                sql.append(" and (pa.flastupdatetime >= ? or pd.flastupdatetime >= ?) ");
+                paramArr.add(modifyDate);
+                paramArr.add(modifyDate);
+            }
+
+            // 员工编号/ID精确匹配
+            if (StringUtils.isNotBlank(code)) {
+                sql.append(" and pae.fpersonid = ? ");
+                paramArr.add(code);
+            }
+
+            // 分页:LIMIT 返回条数 OFFSET 跳过条数
+            sql.append(" LIMIT ? OFFSET ? ");
+            paramArr.add(size);
+            paramArr.add(((page - 1) * size));
+
+            // 执行SQL查询
+            IRowSet rs = DbUtil.executeQuery(context, sql.toString(), paramArr.toArray());
+            // 以员工ID为key的Map,用于合并同一员工的多条记录(一个员工可能有多个上级)
+            Map<String, Map<String, Object>> maps = new HashMap<>();
+            // 日期格式化器(yyyy-MM-dd)
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+            // 遍历结果集,将同一员工的多行数据合并为一条记录
+            while (rs.next()) {
+                String id = rs.getString("personid");
+                Map<String, Object> param = maps.get(id);
+                // 首次遇到该员工时,初始化记录并创建上级ID列表
+                if (null == param) {
+                    param = new HashMap<>();
+                    List<String> leaderIds = new ArrayList<>();
+                    param.put("leader", leaderIds);
+                    maps.put(id, param);
+                }
+                // 收集该员工的所有上级ID(多行数据的共同字段会覆盖,但值相同)
+                List<String> leader = (List<String>) param.get("leader");
+                leader.add(rs.getString("leader"));
+
+                // ---- 员工基本信息 ----
+                // 员工ID
+                param.put("personid", id);
+
+                // ---- 兼职日期(Date -> String 格式化) ----
+                // 兼职开始日期
+                Date startDate = rs.getDate("startDate");
+                param.put("startDate", (null != startDate ? sdf.format(startDate) : null));
+
+                // 移除兼职单审批状态(3表示审批通过)
+                int pdbillstate = rs.getInt("pdbillstate");
+                // 兼职结束日期:只有移除兼职单审批通过时才取实际结束日期,否则返回"2199-12-31"表示兼职未结束
+                Date endDate = rs.getDate("endDate");
+                param.put("endDate", (null != endDate && 3 == pdbillstate ? sdf.format(endDate) : "2199-12-31"));
+
+                // ---- 组织与岗位 ----
+                // 兼职所属组织ID
+                param.put("orgid", rs.getString("orgid"));
+                // 兼职岗位ID
+                param.put("posid", rs.getString("posid"));
+
+                // ---- 备注 ----
+                // 兼职备注(来自加入兼职单分录)
+                param.put("description", rs.getString("description"));
+            }
+            // 将合并后的员工列表写入返回结果
+            result.put("data", maps.values());
+        } catch (Exception e) {
+            // 异常时返回错误信息
+            e.printStackTrace();
+            result.put("msg", e.getMessage());
+            result.put("code", "500");
+        }
+        return result;
+    }
+
+    /**
+     * 构建员工兼职信息查询的基础SQL
+     * <p>
+     *     从员工加入兼职单分录表(T_HR_PluralityAddBizBillEntry)出发,
+     *     LEFT JOIN 加入兼职单表获取审批状态,
+     *     LEFT JOIN 移除兼职单分录表和移除兼职单表获取结束日期和移除审批状态,
+     *     LEFT JOIN 组织岗位表和岗位任职历史表获取当前上级信息。
+     *     只查询加入兼职单审批通过(pa.fbillstate=3)的记录。
+     * </p>
+     *
+     * <h3>核心关联逻辑</h3>
+     * <ol>
+     *     <li>pae → pa:关联加入兼职单,获取审批状态,WHERE条件限定 fbillstate=3(审批通过)</li>
+     *     <li>pae → pde:通过 fpersonid 关联移除兼职单分录,获取兼职结束日期</li>
+     *     <li>pde → pd:关联移除兼职单,获取移除审批状态,用于判断结束日期是否有效</li>
+     *     <li>pae → pos:通过 fpositionid 关联岗位表</li>
+     *     <li>pos → phis:通过 pos.fparentid(上级岗位ID)关联任职历史,取当前有效上级(fpositionenddate >= 2199-12-31)</li>
+     * </ol>
+     *
+     * @return 包含完整SELECT语句的StringBuilder(以 WHERE pa.fbillstate=3 结尾,便于后续追加AND条件)
+     */
+    private static StringBuilder getSql() {
+        StringBuilder sql = new StringBuilder();
+        // /*dialect*/ 标记用于EAS平台识别SQL方言
+        sql.append(" /*dialect*/ select ");
+
+        // ===== SELECT 字段列表 =====
+        sql.append(" pae.fpersonid personid, ");                    // 员工ID
+        sql.append(" pae.fbizdate startDate, ");                     // 兼职开始日期
+        sql.append(" pae.fpositionid posid, ");                      // 兼职岗位ID
+        sql.append(" pae.fadminorgid orgid, ");                      // 兼职所属组织ID
+        sql.append(" phis.fpersonid leader, ");                      // 上级员工ID(通过岗位任职历史关联)
+        sql.append(" pae.fdescription description, ");               // 加入兼职单备注
+        sql.append(" pde.fdescription description2, ");              // 移除兼职单备注(查询用,未返回给调用方)
+        sql.append(" pde.fbizdate endDate, ");                       // 兼职结束日期(来自移除兼职单)
+        sql.append(" pd.fbillstate pdbillstate ");                   // 移除兼职单审批状态(3:审批通过)
+
+        // ===== FROM 子句及关联表 =====
+        sql.append(" from ");
+        sql.append(" T_HR_PluralityAddBizBillEntry pae ");                                                              // 员工加入兼职单分录表(主表)
+        sql.append(" left join T_HR_PluralityAddBizBill pa on pae.fbillid = pa.fid ");                                  // 加入兼职单表(获取审批状态)
+        sql.append(" left join T_HR_PluralityDelBizBillentry pde on pde.FPERSONID = pae.fpersonid ");                   // 移除兼职单分录表(获取结束日期)
+        sql.append(" left join T_HR_PluralityDelBizBill pd on pd.fid = pde.fbillid ");                                   // 移除兼职单表(获取移除审批状态)
+        sql.append(" left join t_org_position pos on pos.fid = pae.fpositionid ");                                      // 组织岗位表(用于关联上级)
+        sql.append(" left join T_HR_EMPPOSTEXPERIENCEHIS phis on phis.fpositionid = pos.fparentid ");                   // 岗位任职历史表(获取当前上级)
+        sql.append(" and phis.fpositionenddate >= '2199-12-31 00:00:00'");                                              // 只取当前有效的任职记录
+
+        // WHERE 条件:只查加入兼职单审批通过的记录,后续可动态追加 AND 条件
+        sql.append(" where pa.fbillstate=3 ");
+        return sql;
+    }
+}