🆕 #2725【企业微信】增加家校沟通-发送「学校通知」的接口

This commit is contained in:
0katekate0 2022-07-06 23:50:32 +08:00 committed by GitHub
parent 918daa2a55
commit f30ac6be6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1188 additions and 4 deletions

View File

@ -10,7 +10,7 @@ import static me.chanjar.weixin.common.error.WxMpErrorMsgEnum.*;
/**
* 微信开发所使用到的常量类.
*
* @author Daniel Qian & binarywang
* @author Daniel Qian & binarywang & Wang_Wong
*/
public class WxConsts {
/**
@ -133,6 +133,54 @@ public class WxConsts {
public static final String MP_NEWS_ARTICLE = "mpnewsarticle";
}
/**
* 发送学校通知类型
* https://developer.work.weixin.qq.com/document/path/92321
*/
public static class SchoolContactMsgType {
/**
* 文本消息.
*/
public static final String TEXT = "text";
/**
* 图片消息.
*/
public static final String IMAGE = "image";
/**
* 语音消息.
*/
public static final String VOICE = "voice";
/**
* 视频消息.
*/
public static final String VIDEO = "video";
/**
* 文件消息
*/
public static final String FILE = "file";
/**
* 图文消息
*/
public static final String NEWS = "news";
/**
* 图文消息mpnews
*/
public static final String MPNEWS = "mpnews";
/**
* 小程序消息
*/
public static final String MINIPROGRAM = "miniprogram";
}
/**
* 企业微信模板卡片消息的卡片类型
*/

View File

@ -50,4 +50,20 @@ public interface WxCpMessageService {
* @throws WxErrorException the wx error exception
*/
WxCpLinkedCorpMessageSendResult sendLinkedCorpMessage(WxCpLinkedCorpMessage message) throws WxErrorException;
/**
* 发送学校通知
* https://developer.work.weixin.qq.com/document/path/92321
* <p>
* 学校可以通过此接口来给家长发送不同类型的学校通知来满足多种场景下的学校通知需求目前支持的消息类型为文本图片语音视频文件图文
* <p>
* 请求方式POSTHTTPS
* 请求地址 https://qyapi.weixin.qq.com/cgi-bin/externalcontact/message/send?access_token=ACCESS_TOKEN
*
* @param message 要发送的消息对象
* @return
* @throws WxErrorException
*/
WxCpSchoolContactMessageSendResult sendSchoolContactMessage(WxCpSchoolContactMessage message) throws WxErrorException;
}

View File

@ -29,6 +29,39 @@ public interface WxCpSchoolUserService {
*/
WxCpBaseResp createStudent(@NonNull String studentUserId, @NonNull String name, @NonNull List<Integer> departments) throws WxErrorException;
/**
* 批量创建学生
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/school/user/batch_create_student?access_token=ACCESS_TOKEN
*
* @param request
* @return
* @throws WxErrorException
*/
WxCpBatchResultList batchCreateStudent(@NonNull WxCpBatchCreateStudentRequest request) throws WxErrorException;
/**
* 批量删除学生
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/school/user/batch_delete_student?access_token=ACCESS_TOKEN
*
* @param request
* @return
* @throws WxErrorException
*/
WxCpBatchResultList batchDeleteStudent(@NonNull WxCpBatchDeleteStudentRequest request) throws WxErrorException;
/**
* 批量更新学生
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/school/user/batch_update_student?access_token=ACCESS_TOKEN
*
* @param request
* @return
* @throws WxErrorException
*/
WxCpBatchResultList batchUpdateStudent(@NonNull WxCpBatchUpdateStudentRequest request) throws WxErrorException;
/**
* 删除学生
* 请求方式GETHTTPS

View File

@ -46,4 +46,15 @@ public class WxCpMessageServiceImpl implements WxCpMessageService {
return WxCpLinkedCorpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage()
.getApiUrl(Message.LINKEDCORP_MESSAGE_SEND), message.toJson()));
}
@Override
public WxCpSchoolContactMessageSendResult sendSchoolContactMessage(WxCpSchoolContactMessage message) throws WxErrorException {
if (null == message.getAgentId()) {
message.setAgentId(this.cpService.getWxCpConfigStorage().getAgentId());
}
return WxCpSchoolContactMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage()
.getApiUrl(Message.EXTERNAL_CONTACT_MESSAGE_SEND), message.toJson()));
}
}

View File

@ -47,6 +47,27 @@ public class WxCpSchoolUserServiceImpl implements WxCpSchoolUserService {
return WxCpBaseResp.fromJson(responseContent);
}
@Override
public WxCpBatchResultList batchCreateStudent(@NonNull WxCpBatchCreateStudentRequest request) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(BATCH_CREATE_STUDENT);
String responseContent = this.cpService.post(apiUrl, request.toJson());
return WxCpBatchResultList.fromJson(responseContent);
}
@Override
public WxCpBatchResultList batchDeleteStudent(@NonNull WxCpBatchDeleteStudentRequest request) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(BATCH_DELETE_STUDENT);
String responseContent = this.cpService.post(apiUrl, request.toJson());
return WxCpBatchResultList.fromJson(responseContent);
}
@Override
public WxCpBatchResultList batchUpdateStudent(@NonNull WxCpBatchUpdateStudentRequest request) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(BATCH_UPDATE_STUDENT);
String responseContent = this.cpService.post(apiUrl, request.toJson());
return WxCpBatchResultList.fromJson(responseContent);
}
@Override
public WxCpBaseResp deleteStudent(@NonNull String studentUserId) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(DELETE_STUDENT) + studentUserId;

View File

@ -20,18 +20,22 @@ import java.io.Serializable;
@NoArgsConstructor
public class NewArticle implements Serializable {
private static final long serialVersionUID = 4087852055781140659L;
/**
* 标题不超过128个字节超过会自动截断
*/
private String title;
/**
* 描述不超过512个字节超过会自动截断
*/
private String description;
/**
* 点击后跳转的链接
*/
private String url;
/**
* 图文消息的图片链接支持JPGPNG格式较好的效果为大图1068*455小图150*150
*/
@ -42,9 +46,14 @@ public class NewArticle implements Serializable {
*/
private String btnText;
/**小程序appid必须是与当前应用关联的小程序appid和pagepath必须同时填写填写后会忽略url字段**/
/**
* 小程序appid必须是与当前应用关联的小程序appid和pagepath必须同时填写填写后会忽略url字段
*/
private String appid;
/**点击消息卡片后的小程序页面仅限本小程序内的页面。appid和pagepath必须同时填写填写后会忽略url字段**/
/**
* 点击消息卡片后的小程序页面仅限本小程序内的页面appid和pagepath必须同时填写填写后会忽略url字段
*/
private String pagepath;
}

View File

@ -21,6 +21,7 @@ import static me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType.*;
/**
* 互联企业消息.
* https://developer.work.weixin.qq.com/document/path/90250
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* @date 2020-08-30

View File

@ -0,0 +1,311 @@
package me.chanjar.weixin.cp.bean.message;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import me.chanjar.weixin.common.api.WxConsts.SchoolContactMsgType;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import org.apache.commons.lang3.ArrayUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static me.chanjar.weixin.common.api.WxConsts.SchoolContactMsgType.*;
/**
* 发送学校通知
* https://developer.work.weixin.qq.com/document/path/92321
*
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2022-06-29
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxCpSchoolContactMessage implements Serializable {
private static final long serialVersionUID = 8833792280163704238L;
/**
* 指定发送对象0表示发送给家长1表示发送给学生2表示发送给家长和学生默认为0
*/
@SerializedName("recv_scope")
private Integer recvScope = 0;
/**
* 家校通讯录家长列表recv_scope为0或2表示发送给对应的家长recv_scope为1忽略最多支持1000个
*/
@SerializedName("to_parent_userid")
private String[] toParentUserId;
/**
* 家校通讯录学生列表recv_scope为0表示发送给学生的所有家长recv_scope为1表示发送给学生recv_scope为2表示发送给学生和学生的所有家长最多支持1000个
*/
@SerializedName("to_student_userid")
private String[] toStudentUserId;
/**
* 家校通讯录部门列表recv_scope为0表示发送给班级的所有家长recv_scope为1表示发送给班级的所有学生recv_scope为2表示发送给班级的所有学生和家长最多支持100个
*/
@SerializedName("to_party")
private String[] toParty;
/**
* 1表示字段生效0表示字段无效recv_scope为0表示发送给学校的所有家长recv_scope为1表示发送给学校的所有学生recv_scope为2表示发送给学校的所有学生和家长默认为0
*/
@SerializedName("toall")
private Boolean toAll = false;
/**
* 消息类型
*/
@SerializedName("msgtype")
private String msgType;
/**
* 企业应用的id整型可在应用的设置页面查看
*/
@SerializedName("agentid")
private Integer agentId;
/**
* 消息内容最长不超过2048个字节支持id转译
*/
@SerializedName("content")
private String content;
/**
* enable_id_trans
* 表示是否开启id转译0表示否1表示是默认0
*/
@SerializedName("enable_id_trans")
private Boolean enableIdTrans = false;
/**
* enable_duplicate_check
* 表示是否开启重复消息检查0表示否1表示是默认0
*/
@SerializedName("enable_duplicate_check")
private Boolean enableDuplicateCheck = false;
/**
* duplicate_check_interval
* 表示是否重复消息检查的时间间隔默认1800s最大不超过4小时
*/
@SerializedName("duplicate_check_interval")
private Integer duplicateCheckInterval;
/**
* 图片媒体文件id可以调用上传临时素材接口获取
*/
@SerializedName("media_id")
private String mediaId;
/**
* 视频消息的标题不超过128个字节超过会自动截断
*/
@SerializedName("title")
private String title;
/**
* 视频消息的描述不超过512个字节超过会自动截断
*/
@SerializedName("description")
private String description;
/**
* 小程序消息封面的mediaid封面图建议尺寸为520*416
*/
@SerializedName("thumb_media_id")
private String thumbMediaId;
/**
* 小程序appid必须是关联到企业的小程序应用
*/
@SerializedName("appid")
private String appId;
/**
* 点击消息卡片后进入的小程序页面路径
*/
@SerializedName("pagepath")
private String pagePath;
/**
* 图文消息
* https://developer.work.weixin.qq.com/document/path/92321#%E5%9B%BE%E6%96%87%E6%B6%88%E6%81%AF
*/
private List<NewArticle> articles = new ArrayList<>();
/**
* 图文消息mpnews
* https://developer.work.weixin.qq.com/document/path/92321#%E5%9B%BE%E6%96%87%E6%B6%88%E6%81%AF%EF%BC%88mpnews%EF%BC%89
* <p>
* mpnews类型的图文消息跟普通的图文消息一致唯一的差异是图文内容存储在企业微信
* 多次发送mpnews会被认为是不同的图文阅读点赞的统计会被分开计算
*/
private List<MpnewsArticle> mpNewsArticles = new ArrayList<>();
/**
* <pre>
* 请使用.
* {@link SchoolContactMsgType#TEXT}
* {@link SchoolContactMsgType#IMAGE}
* {@link SchoolContactMsgType#VOICE}
* {@link SchoolContactMsgType#VIDEO}
* {@link SchoolContactMsgType#NEWS}
* {@link SchoolContactMsgType#MPNEWS}
* {@link SchoolContactMsgType#MINIPROGRAM}
* </pre>
*
* @param msgType 消息类型
*/
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String toJson() {
JsonObject messageJson = new JsonObject();
if (this.getRecvScope() != null) {
messageJson.addProperty("recv_scope", this.getRecvScope());
}
if (ArrayUtils.isNotEmpty(this.getToParentUserId())) {
messageJson.add("to_parent_userid", WxGsonBuilder.create().toJsonTree(this.getToParentUserId()));
}
if (ArrayUtils.isNotEmpty(this.getToStudentUserId())) {
messageJson.add("to_student_userid", WxGsonBuilder.create().toJsonTree(this.getToStudentUserId()));
}
if (ArrayUtils.isNotEmpty(this.getToParty())) {
messageJson.add("to_party", WxGsonBuilder.create().toJsonTree(this.getToParty()));
}
if (this.getToAll() != null) {
messageJson.addProperty("toall", this.getToAll() ? 1 : 0);
}
messageJson.addProperty("msgtype", this.getMsgType());
if (this.getAgentId() != null) {
messageJson.addProperty("agentid", this.getAgentId());
}
if (this.getEnableIdTrans() != null && this.getEnableIdTrans()) {
messageJson.addProperty("enable_id_trans", 1);
}
if (this.getEnableDuplicateCheck() != null && this.getEnableDuplicateCheck()) {
messageJson.addProperty("enable_duplicate_check", 1);
}
if (this.getDuplicateCheckInterval() != null) {
messageJson.addProperty("duplicate_check_interval", this.getDuplicateCheckInterval());
}
this.handleMsgType(messageJson);
return messageJson.toString();
}
/**
* 封装消息类型
*
* @param messageJson
*/
private void handleMsgType(JsonObject messageJson) {
switch (this.getMsgType()) {
case TEXT: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("text", text);
break;
}
case IMAGE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("image", image);
break;
}
case FILE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("file", image);
break;
}
case VOICE: {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", this.getMediaId());
messageJson.add("voice", voice);
break;
}
case VIDEO: {
JsonObject video = new JsonObject();
video.addProperty("media_id", this.getMediaId());
video.addProperty("title", this.getTitle());
video.addProperty("description", this.getDescription());
messageJson.add("video", video);
break;
}
case NEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : this.getArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("description", article.getDescription());
articleJson.addProperty("url", article.getUrl());
articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
break;
}
case MPNEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (MpnewsArticle article : this.getMpNewsArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("thumb_media_id", article.getThumbMediaId());
articleJson.addProperty("author", article.getAuthor());
articleJson.addProperty("content_source_url", article.getContentSourceUrl());
articleJson.addProperty("content", article.getContent());
articleJson.addProperty("digest", article.getDigest());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("mpnews", newsJsonObject);
break;
}
case MINIPROGRAM: {
JsonObject miniprogram = new JsonObject();
miniprogram.addProperty("appid", this.getAppId());
miniprogram.addProperty("pagepath", this.getPagePath());
miniprogram.addProperty("title", this.getTitle());
miniprogram.addProperty("thumb_media_id", this.getThumbMediaId());
messageJson.add("miniprogram", miniprogram);
break;
}
default: {
// do nothing
}
}
}
}

View File

@ -0,0 +1,36 @@
package me.chanjar.weixin.cp.bean.message;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* 发送学校通知返回实体
* https://developer.work.weixin.qq.com/document/path/92321
*
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2022-06-29
*/
@Data
public class WxCpSchoolContactMessageSendResult extends WxCpBaseResp {
private static final long serialVersionUID = 3990693822996824333L;
@SerializedName("invalid_parent_userid")
private String[] invalidParentUserId;
@SerializedName("invalid_student_userid")
private String[] invalidStudentUserId;
@SerializedName("invalid_party")
private String[] invalidParty;
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
public static WxCpSchoolContactMessageSendResult fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpSchoolContactMessageSendResult.class);
}
}

View File

@ -0,0 +1,59 @@
package me.chanjar.weixin.cp.bean.school.user;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import lombok.experimental.Accessors;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* 批量创建学生请求.
*
* @author Wang_Wong
* @date 2022-07-01
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxCpBatchCreateStudentRequest implements Serializable {
private static final long serialVersionUID = -4960239393895754138L;
@SerializedName("students")
private List<Student> students;
@Setter
@Getter
public static class Student implements Serializable {
@SerializedName("student_userid")
private String studentUserId;
@SerializedName("name")
private String name;
@SerializedName("department")
private List<Integer> department;
public static Student fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, Student.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}
public static WxCpBatchCreateStudentRequest fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpBatchCreateStudentRequest.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,36 @@
package me.chanjar.weixin.cp.bean.school.user;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import lombok.experimental.Accessors;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* 批量删除学生请求.
*
* @author Wang_Wong
* @date 2022-07-01
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxCpBatchDeleteStudentRequest implements Serializable {
private static final long serialVersionUID = -4960239393895754138L;
@SerializedName("useridlist")
private List<String> userIdList;
public static WxCpBatchDeleteStudentRequest fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpBatchDeleteStudentRequest.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,54 @@
package me.chanjar.weixin.cp.bean.school.user;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* 批量返回结果.
*
* @author Wang_Wong
* @date 2022-07-01
*/
@Data
public class WxCpBatchResultList extends WxCpBaseResp implements Serializable {
private static final long serialVersionUID = -5028321625140879571L;
@SerializedName("result_list")
private List<ResultList> resultList;
@Setter
@Getter
public static class ResultList extends WxCpBaseResp{
@SerializedName("parent_userid")
private String parentUserId;
@SerializedName("student_userid")
private String studentUserId;
public static ResultList fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, ResultList.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}
public static WxCpBatchResultList fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpBatchResultList.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,62 @@
package me.chanjar.weixin.cp.bean.school.user;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import lombok.experimental.Accessors;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* 批量更新学生请求.
*
* @author Wang_Wong
* @date 2022-07-01
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxCpBatchUpdateStudentRequest implements Serializable {
private static final long serialVersionUID = -4960239393895754138L;
@SerializedName("students")
private List<Student> students;
@Setter
@Getter
public static class Student implements Serializable {
@SerializedName("student_userid")
private String studentUserId;
@SerializedName("new_student_userid")
private String newStudentUserId;
@SerializedName("name")
private String name;
@SerializedName("department")
private List<Integer> department;
public static Student fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, Student.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}
public static WxCpBatchUpdateStudentRequest fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpBatchUpdateStudentRequest.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -28,6 +28,7 @@ public interface WxCpApiPathConsts {
* https://work.weixin.qq.com/api/doc/90000/90135/90235
*/
interface Message {
/**
* 发送应用消息
*/
@ -40,8 +41,16 @@ public interface WxCpApiPathConsts {
/**
* 互联企业发送应用消息
* https://developer.work.weixin.qq.com/document/path/90250
*/
String LINKEDCORP_MESSAGE_SEND = "/cgi-bin/linkedcorp/message/send";
/**
* 发送学校通知
* https://developer.work.weixin.qq.com/document/path/92321
*/
String EXTERNAL_CONTACT_MESSAGE_SEND = "/cgi-bin/externalcontact/message/send";
}
interface Agent {
@ -183,6 +192,10 @@ public interface WxCpApiPathConsts {
String GET_STUDENT_CUSTOMIZE_HEALTH_INFO = "/cgi-bin/school/user/get_student_customize_health_info";
String GET_HEALTH_QRCODE = "/cgi-bin/school/user/get_health_qrcode";
String BATCH_CREATE_STUDENT = "/cgi-bin/school/user/batch_create_student";
String BATCH_DELETE_STUDENT = "/cgi-bin/school/user/batch_delete_student";
String BATCH_UPDATE_STUDENT = "/cgi-bin/school/user/batch_update_student";
String CREATE_STUDENT = "/cgi-bin/school/user/create_student";
String DELETE_STUDENT = "/cgi-bin/school/user/delete_student?userid=";
String UPDATE_STUDENT = "/cgi-bin/school/user/update_student";

View File

@ -52,6 +52,86 @@ public class WxCpSchoolUserTest {
final String exUserId = "wmOQpTDwAAJFHrryZ8I8ALLEZuLHIUKA";
// 返回值
String batchResult = "{\n" +
"\t\"errcode\": 1,\n" +
"\t\"errmsg\": \"invalid student_userid: zhangsan\",\n" +
"\t\"result_list\": [\n" +
"\t\t{\n" +
"\t\t\t\"student_userid\": \"zhangsan\",\n" +
"\t\t\t\"errcode\": 1,\n" +
"\t\t\t\"errmsg\": \"invalid student_userid: zhangsan\"\n" +
"\t\t}\n" +
"\t]\n" +
"}";
WxCpBatchResultList batchResultList = WxCpBatchResultList.fromJson(batchResult);
log.info("batchResultList: {}", batchResultList.toJson());
/**
* 批量更新学生
* https://developer.work.weixin.qq.com/document/path/92330
*
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/school/user/batch_update_student?access_token=ACCESS_TOKEN
*/
String batchUpdateStudent = "{\n" +
"\t\"students\":[\n" +
" {\n" +
"\t\t\t\"student_userid\": \"zhangsan\",\n" +
"\t\t\t\"new_student_userid\":\"zhangsan_new\",\n" +
"\t\t\t\"name\": \"张三\",\n" +
"\t\t\t\"department\": [1, 2]\n" +
"\t\t},\n" +
" {\n" +
"\t\t\t\"student_userid\": \"lisi\",\n" +
"\t\t\t\"name\": \"李四\",\n" +
"\t\t\t\"department\": [3, 4]\n" +
"\t\t}\n" +
" ]\n" +
"}";
WxCpBatchUpdateStudentRequest batchUpdateStudentRequest = WxCpBatchUpdateStudentRequest.fromJson(batchUpdateStudent);
WxCpBatchResultList list3 = cpService.getSchoolUserService().batchUpdateStudent(batchUpdateStudentRequest);
log.info("list3: {}", list3.toJson());
/**
* 批量删除学生
* https://developer.work.weixin.qq.com/document/path/92329
*
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/school/user/batch_delete_student?access_token=ACCESS_TOKEN
*/
String batchDeleteStudent = "{\n" +
"\t\"useridlist\": [\"zhangsan\", \"lisi\"]\n" +
"}\n";
WxCpBatchDeleteStudentRequest batchDeleteStudentRequest = WxCpBatchDeleteStudentRequest.fromJson(batchDeleteStudent);
WxCpBatchResultList list2 = cpService.getSchoolUserService().batchDeleteStudent(batchDeleteStudentRequest);
log.info("list2: {}", list2.toJson());
/**
* 批量创建学生
* https://developer.work.weixin.qq.com/document/path/92328
*/
String batchCreateStudent = "{\n" +
"\t\"students\":[\n" +
" {\n" +
"\t\t\t\"student_userid\": \"zhangsan\",\n" +
"\t\t\t\"name\": \"张三\",\n" +
"\t\t\t\"department\": [1, 2]\n" +
"\t\t},\n" +
" {\n" +
"\t\t\t\"student_userid\": \"lisi\",\n" +
"\t\t\t\"name\": \"李四\",\n" +
"\t\t\t\"department\": [3, 4]\n" +
"\t\t}\n" +
" ]\n" +
"}";
WxCpBatchCreateStudentRequest batchCreateStudentRequest = WxCpBatchCreateStudentRequest.fromJson(batchCreateStudent);
WxCpBatchResultList list1 = cpService.getSchoolUserService().batchCreateStudent(batchCreateStudentRequest);
log.info("list1: {}", list1.toJson());
// String changeContact = WxCpConsts.EventType.CHANGE_CONTACT;
/**
* 增加变更事件类型

View File

@ -7,7 +7,6 @@ import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.ApiTestModuleWithMockServer;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage;

View File

@ -0,0 +1,395 @@
package me.chanjar.weixin.cp.bean.message;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
import org.testng.annotations.Test;
import java.io.InputStream;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 发送学校通知消息测试类
* https://developer.work.weixin.qq.com/document/path/92321
*
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2022-06-29
*/
@Slf4j
public class WxCpSchoolContactMessageTest {
private static WxCpConfigStorage wxCpConfigStorage;
private static WxCpService cpService;
/**
* 发送学校通知
* 学校可以通过此接口来给家长发送不同类型的学校通知来满足多种场景下的学校通知需求目前支持的消息类型为文本图片语音视频文件图文
*
* https://developer.work.weixin.qq.com/document/path/92321
*
* 消息体类型请参考测试类
* WxCpSchoolContactMessageTest
* {@link WxCpSchoolContactMessageTest}
* @throws WxErrorException
*/
@Test
public void testSendSchoolContactMessage() throws WxErrorException {
InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
wxCpConfigStorage = config;
cpService = new WxCpServiceImpl();
cpService.setWxCpConfigStorage(config);
WxCpSchoolContactMessageSendResult sendResult = this.cpService.getMessageService().sendSchoolContactMessage(
WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.TEXT)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.content("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看<a href=\"http://work.weixin.qq.com\">邮件中心视频实况</a>,聪明避开排队。")
.enableIdTrans(false)
.enableDuplicateCheck(false)
.duplicateCheckInterval(1800)
.build()
);
log.info("sendResult: {}", sendResult.toJson());
}
// WxCpConsts.SchoolContactChangeType
@Test
public void testToJson_text() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.TEXT)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.content("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看<a href=\"http://work.weixin.qq.com\">邮件中心视频实况</a>,聪明避开排队。")
.enableIdTrans(false)
.enableDuplicateCheck(false)
.duplicateCheckInterval(1800)
.build();
WxCpSchoolContactMessage schoolContactMessage1 = new WxCpSchoolContactMessage();
schoolContactMessage1.setMsgType(WxConsts.SchoolContactMsgType.TEXT);
schoolContactMessage1.setRecvScope(0);
schoolContactMessage1.setToParentUserId(new String[]{"parent_userid1", "parent_userid2"});
schoolContactMessage1.setToStudentUserId(new String[]{"student_userid1", "student_userid2"});
schoolContactMessage1.setToParty(new String[]{"partyid1", "partyid2"});
schoolContactMessage1.setToAll(false);
schoolContactMessage1.setAgentId(1);
schoolContactMessage1.setContent("你的快递已到,请携带工卡前往邮件中心领取");
schoolContactMessage1.setEnableIdTrans(false);
schoolContactMessage1.setEnableDuplicateCheck(false);
schoolContactMessage1.setDuplicateCheckInterval(1800);
final String jsonMsg = schoolContactMessage1.toJson();
final String json = message.toJson();
String expectedJson = "{\n" +
"\t\"recv_scope\" : 0,\n" +
"\t\"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
"\t\"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
"\t\"to_party\": [\"partyid1\", \"partyid2\"],\n" +
"\t\"toall\" : 0,\n" +
"\t\"msgtype\" : \"text\",\n" +
"\t\"agentid\" : 1,\n" +
"\t\"text\" : {\n" +
"\t\t\"content\" : \"你的快递已到,请携带工卡前往邮件中心领取。\\n出发前可查看<a href=\\\"http://work.weixin.qq.com\\\">邮件中心视频实况</a>,聪明避开排队。\"\n" +
"\t},\n" +
"\t\"enable_id_trans\": 0,\n" +
"\t\"enable_duplicate_check\": 0,\n" +
"\t\"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_image() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.IMAGE)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.mediaId("MEDIA_ID")
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
"\t\"recv_scope\" : 0,\n" +
"\t\"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
"\t\"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
"\t\"to_party\": [\"partyid1\", \"partyid2\"],\n" +
"\t\"toall\" : 0,\n" +
"\t\"msgtype\" : \"image\",\n" +
"\t\"agentid\" : 1,\n" +
"\t\"image\" : {\n" +
"\t\t\"media_id\" : \"MEDIA_ID\"\n" +
"\t},\n" +
"\t\"enable_duplicate_check\": 0,\n" +
"\t\"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_voice() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.VOICE)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.mediaId("MEDIA_ID")
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
"\t\"recv_scope\" : 0,\n" +
"\t\"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
"\t\"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
"\t\"to_party\": [\"partyid1\", \"partyid2\"],\n" +
"\t\"toall\" : 0,\n" +
"\t\"msgtype\" : \"voice\",\n" +
"\t\"agentid\" : 1,\n" +
"\t\"voice\" : {\n" +
"\t\t\"media_id\" : \"MEDIA_ID\"\n" +
"\t},\n" +
"\t\"enable_duplicate_check\": 0,\n" +
"\t\"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_video() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.VIDEO)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.mediaId("MEDIA_ID")
.title("Title")
.description("Description")
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
"\t\"recv_scope\" : 0,\n" +
"\t\"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
"\t\"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
"\t\"to_party\": [\"partyid1\", \"partyid2\"],\n" +
"\t\"toall\" : 0,\n" +
"\t\"msgtype\" : \"video\",\n" +
"\t\"agentid\" : 1,\n" +
"\t\"video\" : {\n" +
" \"media_id\" : \"MEDIA_ID\",\n" +
" \"title\" : \"Title\",\n" +
" \"description\" : \"Description\"\n" +
"\t},\n" +
"\t\"enable_duplicate_check\": 0,\n" +
"\t\"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_file() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.FILE)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.mediaId("1Yv-zXfHjSjU-7LH-GwtYqDGS-zz6w22KmWAT5COgP7o")
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
"\t\"recv_scope\" : 0,\n" +
"\t\"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
"\t\"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
"\t\"to_party\": [\"partyid1\", \"partyid2\"],\n" +
"\t\"toall\" : 0,\n" +
"\t\"msgtype\" : \"file\",\n" +
"\t\"agentid\" : 1,\n" +
"\t\"file\" : {\n" +
" \"media_id\" : \"1Yv-zXfHjSjU-7LH-GwtYqDGS-zz6w22KmWAT5COgP7o\"\n" +
"\t},\n" +
"\t\"enable_duplicate_check\": 0,\n" +
"\t\"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_news() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.NEWS)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.articles(Lists.newArrayList(NewArticle.builder()
.title("中秋节礼品领取")
.description("今年中秋节公司有豪礼相送")
.url("URL")
.picUrl("http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png")
.build()))
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
" \"recv_scope\" : 0,\n" +
" \"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
" \"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
" \"to_party\": [\"partyid1\", \"partyid2\"],\n" +
" \"toall\" : 0,\n" +
" \"msgtype\" : \"news\",\n" +
" \"agentid\" : 1,\n" +
" \"news\" : {\n" +
" \"articles\" : [\n" +
" {\n" +
" \"title\" : \"中秋节礼品领取\",\n" +
" \"description\" : \"今年中秋节公司有豪礼相送\",\n" +
" \"url\" : \"URL\",\n" +
" \"picurl\" : \"http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png\"\n" +
" }\n" +
"\t\t]\n" +
" },\n" +
" \"enable_id_trans\": 0,\n" +
" \"enable_duplicate_check\": 0,\n" +
" \"duplicate_check_interval\": 1800\n" +
"}";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_mpnews() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.MPNEWS)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.mpNewsArticles(Lists.newArrayList(MpnewsArticle.newBuilder()
.title("Title")
.thumbMediaId("MEDIA_ID")
.author("Author")
.contentSourceUrl("URL")
.content("Content")
.digest("Digest description")
.build()))
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
" \"recv_scope\" : 0,\n" +
" \"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
" \"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
" \"to_party\": [\"partyid1\", \"partyid2\"],\n" +
" \"toall\" : 0,\n" +
" \"msgtype\" : \"mpnews\",\n" +
" \"agentid\" : 1,\n" +
" \"mpnews\" : {\n" +
" \"articles\":[\n" +
" {\n" +
" \"title\": \"Title\", \n" +
" \"thumb_media_id\": \"MEDIA_ID\",\n" +
" \"author\": \"Author\",\n" +
" \"content_source_url\": \"URL\",\n" +
" \"content\": \"Content\",\n" +
" \"digest\": \"Digest description\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"enable_id_trans\": 0,\n" +
" \"enable_duplicate_check\": 0,\n" +
" \"duplicate_check_interval\": 1800\n" +
"}\n";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
@Test
public void testToJson_miniProgram() {
WxCpSchoolContactMessage message = WxCpSchoolContactMessage.builder()
.recvScope(0)
.msgType(WxConsts.SchoolContactMsgType.MINIPROGRAM)
.toParentUserId(new String[]{"parent_userid1", "parent_userid2"})
.toStudentUserId(new String[]{"student_userid1", "student_userid2"})
.toParty(new String[]{"partyid1", "partyid2"})
.toAll(false)
.agentId(1)
.appId("APPID")
.title("欢迎报名夏令营")
.thumbMediaId("MEDIA_ID")
.pagePath("PAGE_PATH")
.build();
final String json = message.toJson();
String expectedJson = "{\n" +
" \"recv_scope\" : 0,\n" +
" \"to_parent_userid\": [\"parent_userid1\", \"parent_userid2\"],\n" +
" \"to_student_userid\": [\"student_userid1\", \"student_userid2\"],\n" +
" \"to_party\": [\"partyid1\", \"partyid2\"],\n" +
" \"toall\" : 0,\n" +
" \"agentid\" : 1,\n" +
" \"msgtype\" : \"miniprogram\",\n" +
" \"miniprogram\" : {\n" +
" \"appid\": \"APPID\",\n" +
" \"title\": \"欢迎报名夏令营\",\n" +
" \"thumb_media_id\": \"MEDIA_ID\",\n" +
" \"pagepath\": \"PAGE_PATH\"\n" +
" },\n" +
" \"enable_id_trans\": 0,\n" +
" \"enable_duplicate_check\": 0,\n" +
" \"duplicate_check_interval\": 1800\n" +
"}\n";
assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
}
}