Skip to content

Commit 1286cfd

Browse files
authored
🆕 #4092 【企业微信】增加待办API的支持
1 parent eb42a33 commit 1286cfd

8 files changed

Lines changed: 315 additions & 0 deletions

File tree

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,4 +673,11 @@ public interface WxCpService extends WxService {
673673
* @return 人事助手服务 hr service
674674
*/
675675
WxCpHrService getHrService();
676+
677+
/**
678+
* 获取待办服务
679+
*
680+
* @return 待办服务 todo service
681+
*/
682+
WxCpTodoService getTodoService();
676683
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package me.chanjar.weixin.cp.api;
2+
3+
import me.chanjar.weixin.common.error.WxErrorException;
4+
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
5+
6+
import java.util.List;
7+
8+
/**
9+
* 企业微信待办接口.
10+
* <p>
11+
* 官方文档:
12+
* <a href="https://developer.work.weixin.qq.com/document/path/101524">获取待办详情</a>,
13+
* <a href="https://developer.work.weixin.qq.com/document/path/101534">更新待办状态</a>
14+
*
15+
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
16+
*/
17+
public interface WxCpTodoService {
18+
/**
19+
* 获取待办详情.
20+
* <p>
21+
* 该接口用于获取指定的待办详情,请求参数仅包含必填的 todo_id,响应直接返回单个待办对象。
22+
* <p>
23+
* 请求方式: POST(HTTPS)
24+
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/todo/get?access_token=ACCESS_TOKEN
25+
*
26+
* @param todoId 待办ID
27+
* @return 待办详情 wx cp todo
28+
* @throws WxErrorException the wx error exception
29+
*/
30+
WxCpTodo get(String todoId) throws WxErrorException;
31+
32+
/**
33+
* 更新待办状态.
34+
* <p>
35+
* 该接口用于修改指定的待办信息,支持修改待办整体状态(status 字段)、待办参与人及其状态(attendees[].userid / status 字段)。
36+
* 仅允许修改当前应用创建的待办,不允许修改已删除的待办。
37+
* <p>
38+
* 请求方式: POST(HTTPS)
39+
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/todo/update?access_token=ACCESS_TOKEN
40+
*
41+
* @param todoId 待办ID
42+
* @param status 待办整体状态,可不传:0 - 完成;1 - 进行中。为 null 时不修改整体状态
43+
* @param attendees 待办参与人列表,最多支持20个参与人。为 null 或空时不修改参与人列表
44+
* @throws WxErrorException the wx error exception
45+
*/
46+
void update(String todoId, Integer status, List<WxCpTodo.Attendee> attendees) throws WxErrorException;
47+
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
7777
private final WxCpCorpGroupService corpGroupService = new WxCpCorpGroupServiceImpl(this);
7878
private final WxCpIntelligentRobotService intelligentRobotService = new WxCpIntelligentRobotServiceImpl(this);
7979
private final WxCpHrService hrService = new WxCpHrServiceImpl(this);
80+
private final WxCpTodoService todoService = new WxCpTodoServiceImpl(this);
8081

8182
/**
8283
* 全局的是否正在刷新access token的锁.
@@ -753,4 +754,9 @@ public WxCpIntelligentRobotService getIntelligentRobotService() {
753754
public WxCpHrService getHrService() {
754755
return this.hrService;
755756
}
757+
758+
@Override
759+
public WxCpTodoService getTodoService() {
760+
return this.todoService;
761+
}
756762
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package me.chanjar.weixin.cp.api.impl;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import me.chanjar.weixin.common.error.WxErrorException;
6+
import me.chanjar.weixin.cp.api.WxCpService;
7+
import me.chanjar.weixin.cp.api.WxCpTodoService;
8+
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
9+
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
10+
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Todo.*;
16+
17+
/**
18+
* 企业微信待办接口实现类.
19+
*
20+
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
21+
*/
22+
@Slf4j
23+
@RequiredArgsConstructor
24+
public class WxCpTodoServiceImpl implements WxCpTodoService {
25+
private final WxCpService cpService;
26+
27+
@Override
28+
public WxCpTodo get(String todoId) throws WxErrorException {
29+
final Map<String, Object> param = new HashMap<>(1);
30+
param.put("todo_id", todoId);
31+
final String response = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(TODO_GET),
32+
WxCpGsonBuilder.create().toJson(param));
33+
return WxCpGsonBuilder.create().fromJson(response, WxCpTodo.class);
34+
}
35+
36+
@Override
37+
public void update(String todoId, Integer status, List<WxCpTodo.Attendee> attendees) throws WxErrorException {
38+
final Map<String, Object> param = new HashMap<>(3);
39+
param.put("todo_id", todoId);
40+
if (status != null) {
41+
param.put("status", status);
42+
}
43+
if (attendees != null && !attendees.isEmpty()) {
44+
param.put("attendees", attendees);
45+
}
46+
47+
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(TODO_UPDATE),
48+
WxCpGsonBuilder.create().toJson(param));
49+
}
50+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package me.chanjar.weixin.cp.bean.todo;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
import lombok.experimental.Accessors;
6+
import me.chanjar.weixin.common.bean.ToJson;
7+
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
8+
9+
import java.io.Serializable;
10+
import java.util.List;
11+
12+
/**
13+
* 待办信息bean.
14+
* <p>
15+
* 官方文档:
16+
* <a href="https://developer.work.weixin.qq.com/document/path/101524">获取待办详情</a>
17+
*
18+
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
19+
*/
20+
@Data
21+
@Accessors(chain = true)
22+
public class WxCpTodo implements Serializable, ToJson {
23+
private static final long serialVersionUID = -1L;
24+
25+
/**
26+
* 待办ID
27+
*/
28+
@SerializedName("todo_id")
29+
private String todoId;
30+
/**
31+
* 待办内容
32+
*/
33+
@SerializedName("content")
34+
private String content;
35+
/**
36+
* 待办创建人ID
37+
*/
38+
@SerializedName("creator")
39+
private String creator;
40+
/**
41+
* 待办状态。
42+
* 0 - 已完成
43+
* 1 - 进行中
44+
* 2 - 已删除
45+
*/
46+
@SerializedName("status")
47+
private Integer status;
48+
/**
49+
* 待办创建时间戳(整型秒数)
50+
*/
51+
@SerializedName("create_time")
52+
private Long createTime;
53+
/**
54+
* 待办参与人列表
55+
*/
56+
@SerializedName("attendees")
57+
private List<Attendee> attendees;
58+
/**
59+
* 待办截止时间戳(整型秒数)
60+
*/
61+
@SerializedName("end_time")
62+
private Long endTime;
63+
/**
64+
* 提醒列表
65+
*/
66+
@SerializedName("reminders")
67+
private List<Reminder> reminders;
68+
69+
@Override
70+
public String toJson() {
71+
return WxCpGsonBuilder.create().toJson(this);
72+
}
73+
74+
/**
75+
* 待办参与人.
76+
*/
77+
@Data
78+
@Accessors(chain = true)
79+
public static class Attendee implements Serializable {
80+
private static final long serialVersionUID = -1L;
81+
82+
/**
83+
* 待办参与人ID
84+
*/
85+
@SerializedName("userid")
86+
private String userid;
87+
/**
88+
* 参与人的待办状态。
89+
* 0 - 完成
90+
* 1 - 进行中
91+
*/
92+
@SerializedName("status")
93+
private Integer status;
94+
}
95+
96+
/**
97+
* 待办提醒.
98+
*/
99+
@Data
100+
@Accessors(chain = true)
101+
public static class Reminder implements Serializable {
102+
private static final long serialVersionUID = -1L;
103+
104+
/**
105+
* 提醒时间戳(整型秒数)
106+
*/
107+
@SerializedName("remind_time")
108+
private Long remindTime;
109+
}
110+
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,4 +1884,22 @@ interface Hr {
18841884
*/
18851885
String UPDATE_EMPLOYEE_FIELD_INFO = "/cgi-bin/hr/update_staff_info";
18861886
}
1887+
1888+
/**
1889+
* 待办相关接口.
1890+
* 官方文档:https://developer.work.weixin.qq.com/document/path/101524
1891+
*/
1892+
interface Todo {
1893+
/**
1894+
* 获取待办详情
1895+
* https://developer.work.weixin.qq.com/document/path/101524
1896+
*/
1897+
String TODO_GET = "/cgi-bin/todo/get";
1898+
1899+
/**
1900+
* 更新待办状态
1901+
* https://developer.work.weixin.qq.com/document/path/101534
1902+
*/
1903+
String TODO_UPDATE = "/cgi-bin/todo/update";
1904+
}
18871905
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package me.chanjar.weixin.cp.api.impl;
2+
3+
import com.google.inject.Inject;
4+
import me.chanjar.weixin.common.error.WxErrorException;
5+
import me.chanjar.weixin.cp.api.ApiTestModule;
6+
import me.chanjar.weixin.cp.api.WxCpService;
7+
import me.chanjar.weixin.cp.bean.todo.WxCpTodo;
8+
import org.testng.annotations.Guice;
9+
import org.testng.annotations.Test;
10+
11+
import java.util.Arrays;
12+
13+
import static org.testng.Assert.assertEquals;
14+
import static org.testng.Assert.assertNotNull;
15+
16+
/**
17+
* 单元测试类.
18+
*
19+
* @author <a href="https://github.com/binarywang">Binary Wang</a> created on 2026-08-11
20+
*/
21+
@Guice(modules = ApiTestModule.class)
22+
public class WxCpTodoServiceImplTest {
23+
/**
24+
* The Wx service.
25+
*/
26+
@Inject
27+
protected WxCpService wxService;
28+
29+
private static final String TODO_ID = "17c7d2bd9f20d652840f72f59e796AAA";
30+
31+
/**
32+
* Test get.
33+
*
34+
* @throws WxErrorException the wx error exception
35+
*/
36+
@Test
37+
public void testGet() throws WxErrorException {
38+
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
39+
assertNotNull(todo, "get() 返回的待办对象不应为 null");
40+
assertEquals(todo.getTodoId(), TODO_ID, "返回的 todo_id 应与请求一致");
41+
}
42+
43+
/**
44+
* Test update status only.
45+
*
46+
* @throws WxErrorException the wx error exception
47+
*/
48+
@Test
49+
public void testUpdateStatusOnly() throws WxErrorException {
50+
this.wxService.getTodoService().update(TODO_ID, 0, null);
51+
// 更新成功后通过 get() 回查,验证整体状态确实写入
52+
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
53+
assertNotNull(todo, "回查待办不应为 null");
54+
assertEquals(todo.getStatus(), Integer.valueOf(0), "待办整体状态应为 0(完成)");
55+
}
56+
57+
/**
58+
* Test update with attendees.
59+
*
60+
* @throws WxErrorException the wx error exception
61+
*/
62+
@Test
63+
public void testUpdateWithAttendees() throws WxErrorException {
64+
this.wxService.getTodoService().update(TODO_ID, 1,
65+
Arrays.asList(
66+
new WxCpTodo.Attendee().setUserid("lisi").setStatus(0),
67+
new WxCpTodo.Attendee().setUserid("zhangsan").setStatus(1)
68+
));
69+
// 更新成功后通过 get() 回查,验证参与人列表确实写入
70+
final WxCpTodo todo = this.wxService.getTodoService().get(TODO_ID);
71+
assertNotNull(todo, "回查待办不应为 null");
72+
assertNotNull(todo.getAttendees(), "attendees 列表不应为 null");
73+
assertEquals(todo.getAttendees().size(), 2, "参与人数量应为 2");
74+
assertEquals(todo.getStatus(), Integer.valueOf(1), "待办整体状态应为 1(进行中)");
75+
}
76+
}

weixin-java-cp/src/test/resources/testng.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutVideoMessageTest"/>
2525
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutVoiceMessageTest"/>
2626
<class name="me.chanjar.weixin.cp.bean.message.WxCpXmlOutTextMessageTest"/>
27+
<class name="me.chanjar.weixin.cp.bean.todo.WxCpTodoServiceMockTest"/>
2728
</classes>
2829
</test>
2930
</suite>

0 commit comments

Comments
 (0)