1
0
mirror of synced 2026-03-31 01:58:43 +08:00

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

This commit is contained in:
0katekate0
2022-07-06 23:50:32 +08:00
committed by GitHub
parent 918daa2a55
commit f30ac6be6c
17 changed files with 1188 additions and 4 deletions

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;
/**
* 图文消息的图片链接支持JPG、PNG格式较好的效果为大图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";