Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -673,4 +673,11 @@ public interface WxCpService extends WxService {
* @return 人事助手服务 hr service
*/
WxCpHrService getHrService();

/**
* 获取待办服务
*
* @return 待办服务 todo service
*/
WxCpTodoService getTodoService();
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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的锁.
Expand Down Expand Up @@ -753,4 +754,9 @@ public WxCpIntelligentRobotService getIntelligentRobotService() {
public WxCpHrService getHrService() {
return this.hrService;
}

@Override
public WxCpTodoService getTodoService() {
return this.todoService;
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1884,4 +1884,22 @@ interface Hr {
*/
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";
}
}
Original file line number Diff line number Diff line change
@@ -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(进行中)");
}
}
1 change: 1 addition & 0 deletions weixin-java-cp/src/test/resources/testng.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,17 @@ public String getConfigKey(String mchId, String appId) {
@Override
public String getPayBaseUrl() {
if (this.getConfig().isUseSandboxEnv()) {
if (StringUtils.isNotBlank(this.getConfig().getApiV3Key())) {
throw new WxRuntimeException("微信支付V3 目前不支持沙箱模式!");
}
return this.getConfig().getApiHostWithPathPrefix() + "/xdc/apiv2sandbox";
}
Comment thread
binarywang marked this conversation as resolved.
return this.getConfig().getApiHostWithPathPrefix();
}

protected void checkV3SandboxNotSupported() {
if (this.getConfig().isUseSandboxEnv()) {
throw new WxRuntimeException("微信支付V3 目前不支持沙箱模式!");
}
}

@Override
public WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException {
request.checkAndSign(this.getConfig());
Expand Down
Loading