🆕 #3892 【企业微信】实现企业微信人事助手 API

This commit is contained in:
Copilot
2026-02-28 17:01:23 +08:00
committed by GitHub
parent 8ef5a33dd5
commit 389f1785b6
12 changed files with 660 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldData;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldDataResp;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldInfoResp;
import java.util.List;
/**
* 人事助手相关接口.
* 官方文档https://developer.work.weixin.qq.com/document/path/99132
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
public interface WxCpHrService {
/**
* 获取员工档案字段信息.
* <p>
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/hr/employee/get_field_info?access_token=ACCESS_TOKEN
* 权限说明:
* 需要配置人事助手的secret调用接口前需给对应成员赋予人事小助手应用的权限。
*
* @param fields 指定字段key列表不填则返回全部字段
* @return 字段信息响应 wx cp hr employee field info resp
* @throws WxErrorException the wx error exception
*/
WxCpHrEmployeeFieldInfoResp getFieldInfo(List<String> fields) throws WxErrorException;
/**
* 获取员工档案数据.
* <p>
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/hr/employee/get_employee_field_info?access_token=ACCESS_TOKEN
* 权限说明:
* 需要配置人事助手的secret调用接口前需给对应成员赋予人事小助手应用的权限。
*
* @param userids 员工userid列表不超过20个
* @param fields 指定字段key列表不填则返回全部字段
* @return 员工档案数据响应 wx cp hr employee field data resp
* @throws WxErrorException the wx error exception
*/
WxCpHrEmployeeFieldDataResp getEmployeeFieldInfo(List<String> userids, List<String> fields) throws WxErrorException;
/**
* 更新员工档案数据.
* <p>
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/hr/employee/update_employee_field_info?access_token=ACCESS_TOKEN
* 权限说明:
* 需要配置人事助手的secret调用接口前需给对应成员赋予人事小助手应用的权限。
*
* @param userid 员工userid
* @param fieldList 字段数据列表
* @throws WxErrorException the wx error exception
*/
void updateEmployeeFieldInfo(String userid, List<WxCpHrEmployeeFieldData.FieldItem> fieldList) throws WxErrorException;
}

View File

@@ -594,4 +594,11 @@ public interface WxCpService extends WxService {
* @return 智能机器人服务 intelligent robot service
*/
WxCpIntelligentRobotService getIntelligentRobotService();
/**
* 获取人事助手服务
*
* @return 人事助手服务 hr service
*/
WxCpHrService getHrService();
}

View File

@@ -75,6 +75,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
private final WxCpMeetingService meetingService = new WxCpMeetingServiceImpl(this);
private final WxCpCorpGroupService corpGroupService = new WxCpCorpGroupServiceImpl(this);
private final WxCpIntelligentRobotService intelligentRobotService = new WxCpIntelligentRobotServiceImpl(this);
private final WxCpHrService hrService = new WxCpHrServiceImpl(this);
/**
* 全局的是否正在刷新access token的锁.
@@ -708,4 +709,9 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
public WxCpIntelligentRobotService getIntelligentRobotService() {
return this.intelligentRobotService;
}
@Override
public WxCpHrService getHrService() {
return this.hrService;
}
}

View File

@@ -0,0 +1,77 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpHrService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldData;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldDataResp;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldInfoResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Hr.*;
/**
* 人事助手相关接口实现类.
* 官方文档https://developer.work.weixin.qq.com/document/path/99132
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@RequiredArgsConstructor
public class WxCpHrServiceImpl implements WxCpHrService {
private final WxCpService cpService;
@Override
public WxCpHrEmployeeFieldInfoResp getFieldInfo(List<String> fields) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
if (fields != null && !fields.isEmpty()) {
jsonObject.add("fields", WxCpGsonBuilder.create().toJsonTree(fields));
}
String response = this.cpService.post(
this.cpService.getWxCpConfigStorage().getApiUrl(GET_FIELD_INFO),
jsonObject.toString()
);
return WxCpHrEmployeeFieldInfoResp.fromJson(response);
}
@Override
public WxCpHrEmployeeFieldDataResp getEmployeeFieldInfo(List<String> userids, List<String> fields) throws WxErrorException {
if (userids == null || userids.isEmpty()) {
throw new IllegalArgumentException("userids 不能为空");
}
if (userids.size() > 20) {
throw new IllegalArgumentException("userids 每次最多传入20个");
}
JsonObject jsonObject = new JsonObject();
jsonObject.add("userids", WxCpGsonBuilder.create().toJsonTree(userids));
if (fields != null && !fields.isEmpty()) {
jsonObject.add("fields", WxCpGsonBuilder.create().toJsonTree(fields));
}
String response = this.cpService.post(
this.cpService.getWxCpConfigStorage().getApiUrl(GET_EMPLOYEE_FIELD_INFO),
jsonObject.toString()
);
return WxCpHrEmployeeFieldDataResp.fromJson(response);
}
@Override
public void updateEmployeeFieldInfo(String userid, List<WxCpHrEmployeeFieldData.FieldItem> fieldList) throws WxErrorException {
if (userid == null || userid.trim().isEmpty()) {
throw new IllegalArgumentException("userid 不能为空");
}
if (fieldList == null || fieldList.isEmpty()) {
throw new IllegalArgumentException("fieldList 不能为空");
}
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userid", userid);
jsonObject.add("field_list", WxCpGsonBuilder.create().toJsonTree(fieldList));
this.cpService.post(
this.cpService.getWxCpConfigStorage().getApiUrl(UPDATE_EMPLOYEE_FIELD_INFO),
jsonObject.toString()
);
}
}

View File

@@ -0,0 +1,52 @@
package me.chanjar.weixin.cp.bean.hr;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 人事助手-员工档案数据(单个员工).
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Data
@NoArgsConstructor
public class WxCpHrEmployeeFieldData implements Serializable {
private static final long serialVersionUID = 4593693598671765396L;
/**
* 员工userid.
*/
@SerializedName("userid")
private String userid;
/**
* 字段数据列表.
*/
@SerializedName("field_list")
private List<FieldItem> fieldList;
/**
* 字段数据项.
*/
@Data
@NoArgsConstructor
public static class FieldItem implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 字段key.
*/
@SerializedName("field_key")
private String fieldKey;
/**
* 字段值.
*/
@SerializedName("field_value")
private WxCpHrEmployeeFieldValue fieldValue;
}
}

View File

@@ -0,0 +1,38 @@
package me.chanjar.weixin.cp.bean.hr;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* 人事助手-获取员工档案数据响应.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class WxCpHrEmployeeFieldDataResp extends WxCpBaseResp {
private static final long serialVersionUID = 6593693598671765396L;
/**
* 员工档案数据列表.
*/
@SerializedName("employee_field_list")
private List<WxCpHrEmployeeFieldData> employeeFieldList;
/**
* From json wx cp hr employee field data resp.
*
* @param json the json
* @return the wx cp hr employee field data resp
*/
public static WxCpHrEmployeeFieldDataResp fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpHrEmployeeFieldDataResp.class);
}
}

View File

@@ -0,0 +1,103 @@
package me.chanjar.weixin.cp.bean.hr;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 人事助手-员工档案字段信息.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Data
@NoArgsConstructor
public class WxCpHrEmployeeFieldInfo implements Serializable {
private static final long serialVersionUID = 2593693598671765396L;
/**
* 字段key.
*/
@SerializedName("field_key")
private String fieldKey;
/**
* 字段英文名称.
*/
@SerializedName("field_en_name")
private String fieldEnName;
/**
* 字段中文名称.
*/
@SerializedName("field_zh_name")
private String fieldZhName;
/**
* 字段类型.
* 具体取值参见 {@link WxCpHrFieldType}
*/
@SerializedName("field_type")
private Integer fieldType;
/**
* 获取字段类型枚举.
*
* @return 字段类型枚举,未匹配时返回 null
*/
public WxCpHrFieldType getFieldTypeEnum() {
return fieldType == null ? null : WxCpHrFieldType.fromCode(fieldType);
}
/**
* 是否系统字段.
* 0: 否
* 1: 是
*/
@SerializedName("is_sys")
private Integer isSys;
/**
* 字段详情.
*/
@SerializedName("field_detail")
private FieldDetail fieldDetail;
/**
* 字段详情.
*/
@Data
@NoArgsConstructor
public static class FieldDetail implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 选项列表(单选/多选字段专用).
*/
@SerializedName("option_list")
private List<Option> optionList;
}
/**
* 选项.
*/
@Data
@NoArgsConstructor
public static class Option implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 选项key.
*/
@SerializedName("key")
private String key;
/**
* 选项值.
*/
@SerializedName("value")
private String value;
}
}

View File

@@ -0,0 +1,38 @@
package me.chanjar.weixin.cp.bean.hr;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* 人事助手-获取员工档案字段信息响应.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class WxCpHrEmployeeFieldInfoResp extends WxCpBaseResp {
private static final long serialVersionUID = 5593693598671765396L;
/**
* 字段信息列表.
*/
@SerializedName("field_info_list")
private List<WxCpHrEmployeeFieldInfo> fieldInfoList;
/**
* From json wx cp hr employee field info resp.
*
* @param json the json
* @return the wx cp hr employee field info resp
*/
public static WxCpHrEmployeeFieldInfoResp fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpHrEmployeeFieldInfoResp.class);
}
}

View File

@@ -0,0 +1,94 @@
package me.chanjar.weixin.cp.bean.hr;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 人事助手-员工档案字段值.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Data
@NoArgsConstructor
public class WxCpHrEmployeeFieldValue implements Serializable {
private static final long serialVersionUID = 3593693598671765396L;
/**
* 文本/数字/手机/邮箱类型字段值.
*/
@SerializedName("text_value")
private String textValue;
/**
* 单选类型字段值.
*/
@SerializedName("option_value")
private OptionValue optionValue;
/**
* 多选类型字段值列表.
*/
@SerializedName("option_value_list")
private List<OptionValue> optionValueList;
/**
* 日期类型字段值.
*/
@SerializedName("date_value")
private DateValue dateValue;
/**
* 附件类型字段值.
*/
@SerializedName("attachment_value")
private AttachmentValue attachmentValue;
/**
* 单选/多选选项值.
*/
@Data
@NoArgsConstructor
public static class OptionValue implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 选项key.
*/
@SerializedName("key")
private String key;
}
/**
* 日期值.
*/
@Data
@NoArgsConstructor
public static class DateValue implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 时间戳(字符串格式).
*/
@SerializedName("timestamp")
private String timestamp;
}
/**
* 附件值.
*/
@Data
@NoArgsConstructor
public static class AttachmentValue implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 附件id列表.
*/
@SerializedName("id_list")
private List<String> idList;
}
}

View File

@@ -0,0 +1,64 @@
package me.chanjar.weixin.cp.bean.hr;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 人事助手-员工档案字段类型枚举.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Getter
@AllArgsConstructor
public enum WxCpHrFieldType {
/**
* 文本.
*/
TEXT("文本", 1),
/**
* 日期.
*/
DATE("日期", 2),
/**
* 数字.
*/
NUMBER("数字", 3),
/**
* 单选.
*/
SINGLE_SELECT("单选", 4),
/**
* 多选.
*/
MULTI_SELECT("多选", 5),
/**
* 附件.
*/
ATTACHMENT("附件", 6),
/**
* 手机.
*/
PHONE("手机", 7),
/**
* 邮箱.
*/
EMAIL("邮箱", 8);
private final String typeName;
private final int code;
/**
* 根据数值获取字段类型枚举.
*
* @param code 字段类型数值
* @return 字段类型枚举,未匹配时返回 null
*/
public static WxCpHrFieldType fromCode(int code) {
for (WxCpHrFieldType type : WxCpHrFieldType.values()) {
if (type.code == code) {
return type;
}
}
return null;
}
}

View File

@@ -1701,4 +1701,25 @@ public interface WxCpApiPathConsts {
*/
String SEND_MESSAGE = "/cgi-bin/intelligent_robot/send_message";
}
/**
* 人事助手相关接口.
* 官方文档https://developer.work.weixin.qq.com/document/path/99132
*/
interface Hr {
/**
* 获取员工档案字段信息.
*/
String GET_FIELD_INFO = "/cgi-bin/hr/employee/get_field_info";
/**
* 获取员工档案数据.
*/
String GET_EMPLOYEE_FIELD_INFO = "/cgi-bin/hr/employee/get_employee_field_info";
/**
* 更新员工档案数据.
*/
String UPDATE_EMPLOYEE_FIELD_INFO = "/cgi-bin/hr/employee/update_employee_field_info";
}
}

View File

@@ -0,0 +1,100 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpHrService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldData;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldDataResp;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldInfoResp;
import me.chanjar.weixin.cp.bean.hr.WxCpHrEmployeeFieldValue;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 人事助手接口单元测试类.
*
* @author <a href="https://github.com/leejoker">leejoker</a> created on 2024-01-01
*/
@Slf4j
@Guice(modules = ApiTestModule.class)
public class WxCpHrServiceImplTest {
/**
* The Wx service.
*/
@Inject
private WxCpService wxCpService;
/**
* The Config storage.
*/
@Inject
protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage;
/**
* 测试获取员工档案字段信息.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testGetFieldInfo() throws WxErrorException {
WxCpHrService hrService = this.wxCpService.getHrService();
// 获取所有字段信息
WxCpHrEmployeeFieldInfoResp resp = hrService.getFieldInfo(null);
assertThat(resp).isNotNull();
assertThat(resp.getFieldInfoList()).isNotNull();
log.info("获取所有字段信息: {}", resp);
}
/**
* 测试获取指定字段信息.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testGetFieldInfoWithFilter() throws WxErrorException {
WxCpHrService hrService = this.wxCpService.getHrService();
// 获取指定字段信息
WxCpHrEmployeeFieldInfoResp resp = hrService.getFieldInfo(Collections.singletonList("sys_field_name"));
assertThat(resp).isNotNull();
log.info("获取指定字段信息: {}", resp);
}
/**
* 测试获取员工档案数据.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testGetEmployeeFieldInfo() throws WxErrorException {
WxCpHrService hrService = this.wxCpService.getHrService();
WxCpHrEmployeeFieldDataResp resp = hrService.getEmployeeFieldInfo(
Collections.singletonList(this.configStorage.getUserId()), null);
assertThat(resp).isNotNull();
assertThat(resp.getEmployeeFieldList()).isNotNull();
log.info("获取员工档案数据: {}", resp);
}
/**
* 测试更新员工档案数据.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testUpdateEmployeeFieldInfo() throws WxErrorException {
WxCpHrService hrService = this.wxCpService.getHrService();
WxCpHrEmployeeFieldData.FieldItem fieldItem = new WxCpHrEmployeeFieldData.FieldItem();
fieldItem.setFieldKey("sys_field_name");
WxCpHrEmployeeFieldValue fieldValue = new WxCpHrEmployeeFieldValue();
fieldValue.setTextValue("测试姓名");
fieldItem.setFieldValue(fieldValue);
hrService.updateEmployeeFieldInfo(this.configStorage.getUserId(), Arrays.asList(fieldItem));
log.info("更新员工档案数据成功");
}
}