1
0
mirror of synced 2026-10-15 11:58:09 +08:00

🆕 #4092 【企业微信】增加待办API的支持

This commit is contained in:
softboy99
2026-08-12 13:14:51 +08:00
committed by GitHub
parent eb42a331b3
commit 1286cfd3f3
8 changed files with 315 additions and 0 deletions

View File

@@ -673,4 +673,11 @@ public interface WxCpService extends WxService {
* @return 人事助手服务 hr service
*/
WxCpHrService getHrService();
/**
* 获取待办服务
*
* @return 待办服务 todo service
*/
WxCpTodoService getTodoService();
}

View File

@@ -0,0 +1,47 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
import java.util.List;
/**
* 企业微信待办接口.
* <p>
* 官方文档:
* <a href="https://developer.work.weixin.qq.com/document/path/101524">获取待办详情</a>,
* <a href="https://developer.work.weixin.qq.com/document/path/101534">更新待办状态</a>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
*/
public interface WxCpTodoService {
/**
* 获取待办详情.
* <p>
* 该接口用于获取指定的待办详情,请求参数仅包含必填的 todo_id,响应直接返回单个待办对象。
* <p>
* 请求方式: POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/todo/get?access_token=ACCESS_TOKEN
*
* @param todoId 待办ID
* @return 待办详情 wx cp todo
* @throws WxErrorException the wx error exception
*/
WxCpTodo get(String todoId) throws WxErrorException;
/**
* 更新待办状态.
* <p>
* 该接口用于修改指定的待办信息,支持修改待办整体状态(status 字段)、待办参与人及其状态(attendees[].userid / status 字段)。
* 仅允许修改当前应用创建的待办,不允许修改已删除的待办。
* <p>
* 请求方式: POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/todo/update?access_token=ACCESS_TOKEN
*
* @param todoId 待办ID
* @param status 待办整体状态,可不传:0 - 完成;1 - 进行中。为 null 时不修改整体状态
* @param attendees 待办参与人列表,最多支持20个参与人。为 null 或空时不修改参与人列表
* @throws WxErrorException the wx error exception
*/
void update(String todoId, Integer status, List<WxCpTodo.Attendee> attendees) throws WxErrorException;
}

View File

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

View File

@@ -0,0 +1,50 @@
package me.chanjar.weixin.cp.api.impl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpTodoService;
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Todo.*;
/**
* 企业微信待办接口实现类.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
*/
@Slf4j
@RequiredArgsConstructor
public class WxCpTodoServiceImpl implements WxCpTodoService {
private final WxCpService cpService;
@Override
public WxCpTodo get(String todoId) throws WxErrorException {
final Map<String, Object> param = new HashMap<>(1);
param.put("todo_id", todoId);
final String response = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(TODO_GET),
WxCpGsonBuilder.create().toJson(param));
return WxCpGsonBuilder.create().fromJson(response, WxCpTodo.class);
}
@Override
public void update(String todoId, Integer status, List<WxCpTodo.Attendee> attendees) throws WxErrorException {
final Map<String, Object> param = new HashMap<>(3);
param.put("todo_id", todoId);
if (status != null) {
param.put("status", status);
}
if (attendees != null && !attendees.isEmpty()) {
param.put("attendees", attendees);
}
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(TODO_UPDATE),
WxCpGsonBuilder.create().toJson(param));
}
}

View File

@@ -0,0 +1,110 @@
package me.chanjar.weixin.cp.bean.todo;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.experimental.Accessors;
import me.chanjar.weixin.common.bean.ToJson;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* 待办信息bean.
* <p>
* 官方文档:
* <a href="https://developer.work.weixin.qq.com/document/path/101524">获取待办详情</a>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
*/
@Data
@Accessors(chain = true)
public class WxCpTodo implements Serializable, ToJson {
private static final long serialVersionUID = -1L;
/**
* 待办ID
*/
@SerializedName("todo_id")
private String todoId;
/**
* 待办内容
*/
@SerializedName("content")
private String content;
/**
* 待办创建人ID
*/
@SerializedName("creator")
private String creator;
/**
* 待办状态。
* 0 - 已完成
* 1 - 进行中
* 2 - 已删除
*/
@SerializedName("status")
private Integer status;
/**
* 待办创建时间戳(整型秒数)
*/
@SerializedName("create_time")
private Long createTime;
/**
* 待办参与人列表
*/
@SerializedName("attendees")
private List<Attendee> attendees;
/**
* 待办截止时间戳(整型秒数)
*/
@SerializedName("end_time")
private Long endTime;
/**
* 提醒列表
*/
@SerializedName("reminders")
private List<Reminder> reminders;
@Override
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
/**
* 待办参与人.
*/
@Data
@Accessors(chain = true)
public static class Attendee implements Serializable {
private static final long serialVersionUID = -1L;
/**
* 待办参与人ID
*/
@SerializedName("userid")
private String userid;
/**
* 参与人的待办状态。
* 0 - 完成
* 1 - 进行中
*/
@SerializedName("status")
private Integer status;
}
/**
* 待办提醒.
*/
@Data
@Accessors(chain = true)
public static class Reminder implements Serializable {
private static final long serialVersionUID = -1L;
/**
* 提醒时间戳(整型秒数)
*/
@SerializedName("remind_time")
private Long remindTime;
}
}

View File

@@ -1884,4 +1884,22 @@ public interface WxCpApiPathConsts {
*/
String UPDATE_EMPLOYEE_FIELD_INFO = "/cgi-bin/hr/update_staff_info";
}
/**
* 待办相关接口.
* 官方文档:https://developer.work.weixin.qq.com/document/path/101524
*/
interface Todo {
/**
* 获取待办详情
* https://developer.work.weixin.qq.com/document/path/101524
*/
String TODO_GET = "/cgi-bin/todo/get";
/**
* 更新待办状态
* https://developer.work.weixin.qq.com/document/path/101534
*/
String TODO_UPDATE = "/cgi-bin/todo/update";
}
}

View File

@@ -0,0 +1,76 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import java.util.Arrays;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
/**
* 单元测试类.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
*/
@Guice(modules = ApiTestModule.class)
public class WxCpTodoServiceImplTest {
/**
* The Wx service.
*/
@Inject
protected WxCpService wxService;
private static final String TODO_ID = "17c7d2bd9f20d652840f72f59e796AAA";
/**
* Test get.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testGet() throws WxErrorException {
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
assertNotNull(todo, "get() 返回的待办对象不应为 null");
assertEquals(todo.getTodoId(), TODO_ID, "返回的 todo_id 应与请求一致");
}
/**
* Test update status only.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testUpdateStatusOnly() throws WxErrorException {
this.wxService.getTodoService().update(TODO_ID, 0, null);
// 更新成功后通过 get() 回查,验证整体状态确实写入
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
assertNotNull(todo, "回查待办不应为 null");
assertEquals(todo.getStatus(), Integer.valueOf(0), "待办整体状态应为 0(完成)");
}
/**
* Test update with attendees.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testUpdateWithAttendees() throws WxErrorException {
this.wxService.getTodoService().update(TODO_ID, 1,
Arrays.asList(
new WxCpTodo.Attendee().setUserid("lisi").setStatus(0),
new WxCpTodo.Attendee().setUserid("zhangsan").setStatus(1)
));
// 更新成功后通过 get() 回查,验证参与人列表确实写入
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
assertNotNull(todo, "回查待办不应为 null");
assertNotNull(todo.getAttendees(), "attendees 列表不应为 null");
assertEquals(todo.getAttendees().size(), 2, "参与人数量应为 2");
assertEquals(todo.getStatus(), Integer.valueOf(1), "待办整体状态应为 1(进行中)");
}
}

View File

@@ -24,6 +24,7 @@
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutVideoMessageTest"/>
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutVoiceMessageTest"/>
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutTextMessageTest"/>
<class name="me.chanjar.weixin.cp.bean.todo.WxCpTodoServiceMockTest"/>
</classes>
</test>
</suite>