diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMeetingService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMeetingService.java
new file mode 100644
index 000000000..e3ee86611
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMeetingService.java
@@ -0,0 +1,97 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeeting;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeetingUpdateResult;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpUserMeetingIdResult;
+
+import java.util.List;
+
+/**
+ * 企业微信日程接口.
+ * 企业和开发者通过会议接口可以便捷地预定及管理会议,用于小组周会、部门例会等场景。
+ * 调用接口的应用自动成为会议创建者,也可指定成员作为会议管理员辅助管理。
+ * 官方文档:https://developer.work.weixin.qq.com/document/path/93626
+ *
+ * @author wangmeng3486 created on 2023-01-31
+ */
+public interface WxCpMeetingService {
+ /**
+ * 创建预约会议
+ *
+ * 该接口用于创建一个预约会议。
+ *
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/meeting/create?access_token=ACCESS_TOKEN
+ *
+ * @param meeting the meeting
+ * @return 会议ID string
+ * @throws WxErrorException the wx error exception
+ */
+ String create(WxCpMeeting meeting) throws WxErrorException;
+
+ /**
+ * 修改预约会议
+ *
+ * 该接口用于修改一个指定的预约会议。。
+ *
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/meeting/update?access_token=ACCESS_TOKEN
+ *
+ * @param meeting the meeting
+ * @return wx cp meeting update result
+ * @throws WxErrorException the wx error exception
+ */
+ WxCpMeetingUpdateResult update(WxCpMeeting meeting) throws WxErrorException;
+
+
+ /**
+ * 取消预约会议
+ * 该接口用于取消一个指定的预约会议。
+ *
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/meeting/cancel?access_token=ACCESS_TOKEN
+ *
+ * @param meetingId 会议ID
+ * @throws WxErrorException the wx error exception
+ */
+ void cancel(String meetingId) throws WxErrorException;
+
+ /**
+ * 获取会议详情
+ *
+ * 该接口用于获取指定会议的详情内容。
+ *
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meeting/get?access_token=ACCESS_TOKEN
+ *
+ * @param meetingId the meeting ids
+ * @return the details
+ * @throws WxErrorException the wx error exception
+ */
+ WxCpMeeting getDetail(String meetingId) throws WxErrorException;
+
+ /**
+ * 获取成员会议ID列表
+ * 该接口用于获取指定成员指定时间内的会议ID列表。
+ *
+ * 权限说明:
+ * 只能拉取该应用创建的会议ID
+ * 自建应用需要配置在“可调用接口的应用”列表
+ * 第三方服务商创建应用的时候,需要开启“会议接口权限”
+ * 代开发自建应用需要授权“会议接口权限”
+ *
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/meeting/get_user_meetingid?access_token=ACCESS_TOKEN
+ *
+ * @param userId 企业成员的userid
+ * @param cursor 上一次调用时返回的cursor,初次调用可以填"0"
+ * @param limit 每次拉取的数据量,默认值和最大值都为100
+ * @param beginTime 开始时间
+ * @param endTime 结束时间,时间跨度不超过180天。如果begin_time和end_time都没填的话,默认end_time为当前时间
+ * @return result of listUserMeetingIds
+ * @throws WxErrorException the wx error exception
+ */
+ WxCpUserMeetingIdResult getUserMeetingIds(String userId, String cursor, Integer limit,
+ Long beginTime, Long endTime) throws WxErrorException;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
index 8a9bdb19b..2c38f6fc5 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
@@ -562,4 +562,11 @@ public interface WxCpService extends WxService {
* @param exportService 异步导出服务
*/
void setExportService(WxCpExportService exportService);
+
+ /**
+ * 相关接口的服务类对象
+ *
+ * @return the meeting service
+ */
+ WxCpMeetingService getMeetingService();
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
index dea53f5dc..752360003 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
@@ -70,6 +70,8 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
private WxCpExportService exportService = new WxCpExportServiceImpl(this);
+ private final WxCpMeetingService meetingService = new WxCpMeetingServiceImpl(this);
+
/**
* 全局的是否正在刷新access token的锁.
*/
@@ -665,4 +667,9 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
public void setExportService(WxCpExportService exportService) {
this.exportService = exportService;
}
+
+ @Override
+ public WxCpMeetingService getMeetingService() {
+ return meetingService;
+ }
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImpl.java
new file mode 100644
index 000000000..8ca7961ae
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImpl.java
@@ -0,0 +1,78 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpMeetingService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeeting;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeetingUpdateResult;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpUserMeetingIdResult;
+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.Oa.*;
+
+/**
+ * 企业微信日程接口.
+ * 企业和开发者通过会议接口可以便捷地预定及管理会议,用于小组周会、部门例会等场景。
+ * 调用接口的应用自动成为会议创建者,也可指定成员作为会议管理员辅助管理。
+ * 官方文档:https://developer.work.weixin.qq.com/document/path/93626
+ *
+ * @author wangmeng3486 created on 2023-01-31
+ */
+@RequiredArgsConstructor
+public class WxCpMeetingServiceImpl implements WxCpMeetingService {
+ private final WxCpService cpService;
+
+ @Override
+ public String create(WxCpMeeting meeting) throws WxErrorException {
+ return this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(MEETING_ADD),
+ WxCpGsonBuilder.create().toJson(meeting));
+ }
+
+ @Override
+ public WxCpMeetingUpdateResult update(WxCpMeeting meeting) throws WxErrorException {
+ final String response = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(MEETING_UPDATE),
+ WxCpGsonBuilder.create().toJson(meeting));
+ return WxCpGsonBuilder.create().fromJson(response, WxCpMeetingUpdateResult.class);
+ }
+
+ @Override
+ public void cancel(String meetingId) throws WxErrorException {
+ this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(MEETING_CANCEL),
+ WxCpGsonBuilder.create().toJson(ImmutableMap.of("meetingid", meetingId)));
+ }
+
+ @Override
+ public WxCpMeeting getDetail(String meetingId) throws WxErrorException {
+ final String response = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(MEETING_DETAIL),
+ WxCpGsonBuilder.create().toJson(ImmutableMap.of("meetingid", meetingId)));
+ return WxCpGsonBuilder.create().fromJson(response, WxCpMeeting.class);
+ }
+
+ @Override
+ public WxCpUserMeetingIdResult getUserMeetingIds(String userId, String cursor, Integer limit,
+ Long beginTime, Long endTime) throws WxErrorException {
+ final Map param = new HashMap<>(3);
+ param.put("userid", userId);
+ if (cursor != null) {
+ param.put("cursor", cursor);
+ }
+ if (limit != null) {
+ param.put("limit", limit);
+ }
+ if (limit != null) {
+ param.put("begin_time", beginTime);
+ }
+ if (limit != null) {
+ param.put("end_time", endTime);
+ }
+ final String response = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(GET_USER_MEETING_ID),
+ WxCpGsonBuilder.create().toJson(param));
+ return WxCpGsonBuilder.create().fromJson(response, WxCpUserMeetingIdResult.class);
+ }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeeting.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeeting.java
new file mode 100644
index 000000000..0efbc5a77
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeeting.java
@@ -0,0 +1,305 @@
+package me.chanjar.weixin.cp.bean.oa.meeting;
+
+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.
+ *
+ * @author wangmeng3486 created on 2023-02-02
+ */
+@Data
+@Accessors(chain = true)
+public class WxCpMeeting implements Serializable, ToJson {
+ private static final long serialVersionUID = 957588409099876012L;
+
+ /**
+ * 会议id
+ */
+ @SerializedName("meetingid")
+ private String meetingId;
+ /**
+ * 会议管理员userid
+ */
+ @SerializedName("admin_userid")
+ private String adminUserId;
+ /**
+ * 会议的标题,最多支持40个字节或者20个utf8字符
+ */
+ @SerializedName("title")
+ private String title;
+
+ /**
+ * 会议开始时间的unix时间戳。需大于当前时间
+ */
+ @SerializedName("meeting_start")
+ private Long meetingStart;
+
+ /**
+ * 会议持续时间单位秒,最小300秒
+ */
+ @SerializedName("meeting_duration")
+ private Integer meetingDuration;
+
+ /**
+ * 会议的描述,最多支持500个字节或者utf8字符
+ */
+ @SerializedName("description")
+ private String description;
+
+ /**
+ * 会议地点,最多128个字符
+ */
+ @SerializedName("location")
+ private String location;
+
+ /**
+ * 授权方安装的应用agentid。仅旧的第三方多应用套件需要填此参数
+ */
+ @SerializedName("agentid")
+ private Integer agentId;
+
+
+ /**
+ * 参与会议的成员。会议人数上限,以指定的「管理员」可预约的人数上限来校验,普通企业与会人员最多100;
+ * 付费企业根据企业选购的在线会议室容量。任何userid不合法或者不在应用可见范围,直接报错。
+ */
+ @SerializedName("attendees")
+ private Attendees attendees;
+
+
+ /**
+ * 会议所属日历ID。该日历必须是access_token所对应应用所创建的日历。
+ * 注意,若参与人在日历分享范围内,则插入到该日历(同时会插入会议参与人的默认日历),若不在分享范围内,否则仅插入到参与者默认日历;
+ * 如果不填,那么插入到参与者的默认日历上。
+ * 第三方应用必须指定cal_id
+ * 不多于64字节
+ */
+ @SerializedName("cal_id")
+ private String calId;
+ /**
+ * 会议配置
+ */
+ @SerializedName("settings")
+ private Setting settings;
+
+ /**
+ * 重复会议相关配置
+ */
+ @SerializedName("reminders")
+ private Reminder reminders;
+
+ @SerializedName("meeting_code")
+ private String meetingCode;
+
+ @SerializedName("meeting_link")
+ private String meetingLink;
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ /**
+ * The type Attendee.
+ */
+ @Data
+ @Accessors(chain = true)
+ public static class Attendees implements Serializable {
+ private static final long serialVersionUID = 5419000348428480645L;
+ /**
+ * 会议参与者ID,
+ * 不多于64字节
+ */
+ @SerializedName("userid")
+ private List userId;
+
+ @SerializedName("member")
+ private List member;
+ @SerializedName("tmp_external_user")
+ private List tmpExternalUser;
+
+ /**
+ * 企业内部成员
+ */
+ @Data
+ @Accessors(chain = true)
+ public static class Member implements Serializable {
+ private static final long serialVersionUID = 1001531041411551854L;
+ /**
+ * 企业内部成员的userid
+ */
+ @SerializedName("userid")
+ private String userid;
+
+ /**
+ * 与会状态。1为已参与,2为未参与
+ */
+ @SerializedName("status")
+ private Integer status;
+ /**
+ * 参会人首次加入会议时间的unix时间戳
+ */
+ @SerializedName("first_join_time")
+ private Long firstJoinTime;
+ /**
+ * 参会人最后一次离开会议时间的unix时间戳
+ */
+ @SerializedName("last_quit_time")
+ private Long lastQuitTime;
+ /**
+ * 参会人累计参会时长,单位为秒
+ */
+ @SerializedName("cumulative_time")
+ private Long cumulativeTime;
+
+ }
+
+ /**
+ * 会中参会的外部联系人
+ */
+ @Data
+ @Accessors(chain = true)
+ public static class TmpExternalUser implements Serializable {
+ private static final long serialVersionUID = -1411758297144496040L;
+ /**
+ * 会中入会的外部用户临时id。同一个用户在不同会议中返回的该id不一致。
+ */
+ @SerializedName("tmp_external_userid")
+ private String tmpExternalUserid;
+ /**
+ * 参会人首次加入会议时间的unix时间戳
+ */
+ @SerializedName("first_join_time")
+ private Long firstJoinTime;
+ /**
+ * 参会人最后一次离开会议时间的unix时间戳
+ */
+ @SerializedName("last_quit_time")
+ private Long lastQuitTime;
+
+ /**
+ * 参会人入会次数
+ */
+ @SerializedName("total_join_count")
+ private Integer totalJoinCount;
+
+ /**
+ * 参会人累计参会时长,单位为秒
+ */
+ @SerializedName("cumulative_time")
+ private Integer cumulativeTime;
+ }
+
+ }
+
+
+ /**
+ * The type Reminder.
+ */
+ @Data
+ @Accessors(chain = true)
+ public static class Reminder implements Serializable {
+ private static final long serialVersionUID = -4097232428444045131L;
+ /**
+ * 是否是周期性会议,1:周期性会议 0:非周期性会议。默认为0
+ */
+ @SerializedName("is_repeat")
+ private Integer isRepeat;
+ /**
+ * 周期性会议重复类型,0.每天;1.每周;2.每月;7.每个工作日。默认为0。周期性会议该字段才生效
+ */
+ @SerializedName("repeat_type")
+ private Integer repeatType;
+
+ /**
+ * 重复结束时刻。周期性会议该字段才生效。若会议结束时间超出最大结束时间或者未设置,则默认设置为最大结束时间。
+ * 每天\每个工作日\每周 最多重复200次会议;
+ * 每两周\每月最多重复50次会议
+ */
+ @SerializedName("repeat_until")
+ private Long repeatUntil;
+
+ /**
+ * 重复间隔
+ * 仅当指定为自定义重复时有效
+ * 目前仅当repeat_type为2时,即周期为周时,支持设置该字段,且值不能大于2。
+ */
+ @SerializedName("repeat_interval")
+ private Integer repeatInterval;
+
+ /**
+ * 指定会议开始前多久提醒用户,相对于meeting_start前的秒数,默认不提醒。
+ * 目前仅支持0:会议开始时提醒;300:5分钟前提醒;900:15分钟前提醒;3600:一小时前提醒;86400一天前提醒。
+ * 若指定了非支持的值,则表现为会议开始时提醒
+ */
+ @SerializedName("remind_before")
+ private List remindBefore;
+ }
+
+
+ /**
+ * The Settings.
+ */
+ @Data
+ @Accessors(chain = true)
+ public static class Setting implements Serializable {
+ private static final long serialVersionUID = 5030527150838243356L;
+
+ /**
+ * 入会密码,仅可输入4-6位纯数字
+ */
+ @SerializedName("password")
+ private String password;
+ /**
+ * 是否开启等候室。true:开启等候室;false:不开启等候室;默认不开
+ */
+ @SerializedName("enable_waiting_room")
+ private boolean enableWaitingRoom;
+ /**
+ * 是否允许成员在主持人进会前加入。true:允许;false:不允许。默认允许
+ */
+ @SerializedName("allow_enter_before_host")
+ private boolean allowEnterBeforeHost;
+ /**
+ * 会议开始时来电提醒方式,1.不提醒 2.仅提醒主持人 3.提醒所有成员入 4.指定部分人响铃。默认仅提醒主持人
+ */
+ @SerializedName("remind_scope")
+ private Integer remindScope;
+ /**
+ * 成员入会时静音;1:开启;0:关闭;2:超过6人后自动开启静音。默认超过6人自动开启静音
+ */
+ @SerializedName("enable_enter_mute")
+ private Integer enableEnterMute;
+
+ /**
+ * true:所有成员可入会;false:仅企业内部成员可入会 。默认所有成员可入会
+ */
+ @SerializedName("allow_external_user")
+ private boolean allowExternalUser;
+
+ /**
+ * 是否开启屏幕水印,true:开启;false:不开启。默认不开启
+ */
+ @SerializedName("enable_screen_watermark")
+ private boolean enableScreenWatermark;
+
+ /**
+ * 会议主持人人列表,主持人员最多10个。若包含ceaater_userid,会自动过滤。任何userid不合法,直接报错。
+ */
+ @SerializedName("hosts")
+ private Attendees hosts;
+
+ /**
+ * 指定响铃的用户列表。如果remid_scope为4,但是ring_users为空,则全部成员均不响铃。
+ */
+ @SerializedName("ring_users")
+ private Attendees ringUsers;
+ }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeetingUpdateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeetingUpdateResult.java
new file mode 100644
index 000000000..dfd200f2a
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpMeetingUpdateResult.java
@@ -0,0 +1,41 @@
+package me.chanjar.weixin.cp.bean.oa.meeting;
+
+import com.google.common.base.Splitter;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 为标签添加或移除用户结果对象类.
+ *
+ * @author wangmeng3486 created on 2023-01-31
+ */
+@Data
+public class WxCpMeetingUpdateResult extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -4993287594652231097L;
+
+ @Override
+ public String toString() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ /**
+ * From json wx cp tag add or remove users result.
+ *
+ * @param json the json
+ * @return the wx cp tag add or remove users result
+ */
+ public static WxCpMeetingUpdateResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpMeetingUpdateResult.class);
+ }
+
+ @SerializedName("excess_users")
+ private String[] excessUsers;
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpUserMeetingIdResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpUserMeetingIdResult.java
new file mode 100644
index 000000000..acdd47af0
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meeting/WxCpUserMeetingIdResult.java
@@ -0,0 +1,40 @@
+package me.chanjar.weixin.cp.bean.oa.meeting;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 为标签添加或移除用户结果对象类.
+ *
+ * @author wangmeng3486 created on 2023-01-31
+ */
+@Data
+public class WxCpUserMeetingIdResult extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -4993287594652231097L;
+
+ @Override
+ public String toString() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ /**
+ * From json wx cp tag add or remove users result.
+ *
+ * @param json the json
+ * @return the wx cp tag add or remove users result
+ */
+ public static WxCpUserMeetingIdResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpUserMeetingIdResult.class);
+ }
+
+
+ @SerializedName("meetingid_list")
+ private String[] meetingIdList;
+
+ @SerializedName("next_cursor")
+ private String nextCursor;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index 7baf48e71..c592682af 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -374,6 +374,28 @@ public interface WxCpApiPathConsts {
/**
* 会议
+ * https://developer.work.weixin.qq.com/document/path/93626
+ */
+ String MEETING_ADD = "/cgi-bin/meeting/create";
+ /**
+ * The constant MEETING_UPDATE.
+ */
+ String MEETING_UPDATE = "/cgi-bin/meeting/update";
+ /**
+ * The constant MEETING_CANCEL.
+ */
+ String MEETING_CANCEL = "/cgi-bin/meeting/cancel";
+ /**
+ * The constant MEETING_DETAIL.
+ */
+ String MEETING_DETAIL = "/cgi-bin/meeting/get_info";
+ /**
+ * The constant GET_USER_MEETING_ID.
+ */
+ String GET_USER_MEETING_ID = "/cgi-bin/meeting/get_user_meetingid";
+
+ /**
+ * 会议室
* https://developer.work.weixin.qq.com/document/path/93624
*/
String MEETINGROOM_ADD = "/cgi-bin/oa/meetingroom/add";
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImplTest.java
new file mode 100644
index 000000000..55ca45fc5
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMeetingServiceImplTest.java
@@ -0,0 +1,93 @@
+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.WxCpMeetingService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeeting;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpMeetingUpdateResult;
+import me.chanjar.weixin.cp.bean.oa.meeting.WxCpUserMeetingIdResult;
+import org.eclipse.jetty.util.ajax.JSON;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+import org.testng.collections.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.testng.Assert.assertEquals;
+
+
+/**
+ * 单元测试类.
+ *
+ * @author wangmeng3486 created on 2023-02-03
+ */
+@Test
+@Slf4j
+@Guice(modules = ApiTestModule.class)
+public class WxCpMeetingServiceImplTest {
+ /**
+ * The Wx service.
+ */
+ @Inject
+ private WxCpService wxCpService;
+ /**
+ * The Config storage.
+ */
+ @Inject
+ protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage;
+
+ /**
+ * Test
+ *
+ * @throws WxErrorException the wx error exception
+ */
+ @Test
+ public void testAdd() throws WxErrorException {
+ WxCpMeetingService wxCpMeetingService = this.wxCpService.getMeetingService();
+ /*
+ 测试 创建会议
+ */
+ long startTime = System.currentTimeMillis() / 1000 + 30 * 60L;
+ List userIds = Lists.newArrayList(this.configStorage.getUserId(), "lisi");
+ WxCpMeeting wxCpMeeting = new WxCpMeeting().setAdminUserId(this.configStorage.getUserId()).setTitle("新建会议")
+ .setMeetingStart(startTime).setMeetingDuration(3600).setDescription("新建会议描述").setLocation("广州媒体港")
+ .setAttendees(new WxCpMeeting.Attendees().setUserId(userIds))
+ .setSettings(new WxCpMeeting.Setting().setRemindScope(1).setPassword("1234").setEnableWaitingRoom(false)
+ .setAllowEnterBeforeHost(true).setEnableEnterMute(1).setAllowExternalUser(false).setEnableScreenWatermark(false)
+ .setHosts(new WxCpMeeting.Attendees().setUserId(userIds)).setRingUsers(new WxCpMeeting.Attendees().setUserId(userIds)))
+ .setReminders(new WxCpMeeting.Reminder().setIsRepeat(1).setRepeatType(0).setRepeatUntil(startTime + 24 * 60 * 60L)
+ .setRepeatInterval(1).setRemindBefore(Lists.newArrayList(0, 900)));
+ String meetingId = "hyXG0RCQAAogMgFb9Tx_b-1-lhJRWvvg";// wxCpMeetingService.create(wxCpMeeting);
+ assertThat(meetingId).isNotNull();
+ /*
+ 测试 获取用户会议列表
+ */
+ wxCpMeeting.setMeetingId(meetingId);
+ WxCpUserMeetingIdResult wxCpUserMeetingIdResult = wxCpMeetingService.getUserMeetingIds(this.configStorage.getUserId(), null, null, startTime, null);
+ assertThat(wxCpUserMeetingIdResult.getMeetingIdList()).isNotNull();
+ log.info("获取用户会议列表: {}", wxCpUserMeetingIdResult.toJson());
+ /*
+ 测试 修改会议
+ */
+ wxCpMeeting.setTitle("修改会议");
+ wxCpMeeting.setDescription("修改会议描述");
+ WxCpMeetingUpdateResult wxCpMeetingUpdateResult = wxCpMeetingService.update(wxCpMeeting);
+ assertEquals(wxCpMeetingUpdateResult.getErrcode(), 0L);
+ /*
+ 测试 获取会议详情
+ */
+ WxCpMeeting wxCpMeetingResult = wxCpMeetingService.getDetail(meetingId);
+ log.info("获取会议详情: {}", wxCpMeetingResult.toJson());
+ /*
+ 测试 取消会议
+ */
+ wxCpMeetingService.cancel(meetingId);
+ }
+}