Skip to content

【企业微信】增加家校应用-复学码接口支持 #2676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 5, 2022
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 @@ -38,6 +38,10 @@ public class WxCpProperties {
* 微信企业号应用 EncodingAESKey
*/
private String aesKey;
/**
* 微信企业号应用 会话存档私钥
*/
private String msgAuditPriKey;
/**
* 微信企业号应用 会话存档类库路径
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ protected WxCpDefaultConfigImpl config(WxCpDefaultConfigImpl config, WxCpPropert
String token = properties.getToken();
Integer agentId = properties.getAgentId();
String aesKey = properties.getAesKey();
// 企业微信,会话存档路径
// 企业微信,私钥,会话存档路径
String msgAuditPriKey = properties.getMsgAuditPriKey();
String msgAuditLibPath = properties.getMsgAuditLibPath();

config.setCorpId(corpId);
Expand All @@ -32,6 +33,9 @@ protected WxCpDefaultConfigImpl config(WxCpDefaultConfigImpl config, WxCpPropert
if (StringUtils.isNotBlank(aesKey)) {
config.setAesKey(aesKey);
}
if (StringUtils.isNotBlank(msgAuditPriKey)) {
config.setMsgAuditPriKey(msgAuditPriKey);
}
if (StringUtils.isNotBlank(msgAuditLibPath)) {
config.setMsgAuditLibPath(msgAuditLibPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,26 @@ public interface WxCpMsgAuditService {
* 获取解密的聊天数据Model
*
* @param chatData getChatDatas()获取到的聊天数据
* @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的聊天数据
* @throws Exception
*/
WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;
WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;

/**
* 获取解密的聊天数据明文
*
* @param chatData getChatDatas()获取到的聊天数据
* @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的明文
* @throws Exception
*/
String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;
String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;

/**
* 获取媒体文件
* 针对图片、文件等媒体数据,提供sdk接口拉取数据内容。
*
* <p>
* 注意:
* 根据上面返回的文件类型,拼接好存放文件的绝对路径即可。此时绝对路径写入文件流,来达到获取媒体文件的目的。
* 详情可以看官方文档,亦可阅读此接口源码。
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package me.chanjar.weixin.cp.api;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.school.WxCpCustomizeHealthInfo;
import me.chanjar.weixin.cp.bean.school.WxCpResultList;

import javax.validation.constraints.NotNull;
import java.util.List;

/**
* 企业微信家校应用 复学码相关接口.
* https://developer.work.weixin.qq.com/document/path/93744
* <p>
* 权限说明:
* 仅复学码应用可以调用
*
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date: 2022/5/31 9:10
*/
public interface WxCpSchoolService {

/**
* 获取老师健康信息
* 请求方式: POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/school/user/get_teacher_customize_health_info?access_token=ACCESS_TOKEN
*
* @param date
* @param nextKey
* @param limit
* @return
* @throws WxErrorException
*/
WxCpCustomizeHealthInfo getTeacherCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException;

/**
* 获取学生健康信息
* 请求方式: POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/school/user/get_student_customize_health_info?access_token=ACCESS_TOKEN
*
* @param date
* @param nextKey
* @param limit
* @return
* @throws WxErrorException
*/
WxCpCustomizeHealthInfo getStudentCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException;

/**
* 获取师生健康码
* 请求方式:POST(HTTPS)
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/get_health_qrcode?access_token=ACCESS_TOKEN
*
* @param userIds
* @param type
* @return
* @throws WxErrorException
*/
WxCpResultList getHealthQrCode(@NotNull List<String> userIds, @NotNull Integer type) throws WxErrorException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ public interface WxCpService extends WxService {
*/
WxCpOaService getOaService();

/**
* 获取家校应用复学码相关接口的服务类对象
*
* @return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EmptyBlockTag: A block tag (@param, @return, @throws, @deprecated) has an empty description. Block tags without descriptions don't add much value for future readers of the code; consider removing the tag entirely or adding a description.

Suggested change
* @return
*

(at-me in a reply with help or ignore)


Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]

*/
WxCpSchoolService getSchoolService();

/**
* 获取家校应用健康上报的服务类对象
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
private WxCpTagService tagService = new WxCpTagServiceImpl(this);
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
private WxCpOaService oaService = new WxCpOaServiceImpl(this);
private WxCpSchoolService schoolService = new WxCpSchoolServiceImpl(this);
private WxCpSchoolHealthService schoolHealthService = new WxCpSchoolHealthServiceImpl(this);
private WxCpLivingService livingService = new WxCpLivingServiceImpl(this);
private WxCpOaAgentService oaAgentService = new WxCpOaAgentServiceImpl(this);
Expand Down Expand Up @@ -494,6 +495,11 @@ public WxCpOaService getOaService() {
return oaService;
}

@Override
public WxCpSchoolService getSchoolService() {
return schoolService;
}

@Override
public WxCpSchoolHealthService getSchoolHealthService() {
return schoolHealthService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,46 +97,59 @@ public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, S
Finance.FreeSlice(slice);
WxCpChatDatas chatDatas = WxCpChatDatas.fromJson(content);
if (chatDatas.getErrCode().intValue() != 0) {
Finance.DestroySingletonSDK(sdk);
throw new WxErrorException(chatDatas.toJson());
}

return chatDatas;
}

@Override
public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception {
String plainText = this.decryptChatData(chatData);
public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
String plainText = this.decryptChatData(chatData, pkcs1);
return WxCpChatModel.fromJson(plainText);
}

public String decryptChatData(WxCpChatDatas.WxCpChatData chatData) throws Exception {
// 企业获取的会话内容将用公钥加密,企业可用自行保存的私钥解开会话内容数据,aeskey不能为空
String priKey = cpService.getWxCpConfigStorage().getAesKey();
public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1) throws Exception {
/**
* 企业获取的会话内容,使用企业自行配置的消息加密公钥进行加密,企业可用自行保存的私钥解开会话内容数据。
* msgAuditPriKey 会话存档私钥不能为空
*/
String priKey = cpService.getWxCpConfigStorage().getMsgAuditPriKey();
if (StringUtils.isEmpty(priKey)) {
throw new WxErrorException("请配置会话存档私钥【aesKey】");
throw new WxErrorException("请配置会话存档私钥【msgAuditPriKey】");
}

String decryptByPriKey = WxCpCryptUtil.decryptByPriKey(chatData.getEncryptRandomKey(), priKey);
// 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
String decryptByPriKey = WxCpCryptUtil.decryptPriKey(chatData.getEncryptRandomKey(), priKey, pkcs1);
/**
* 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
*/
long sdk = Finance.SingletonSDK();
long msg = Finance.NewSlice();

/**
* 解密会话存档内容
* sdk不会要求用户传入rsa私钥,保证用户会话存档数据只有自己能够解密。
* 此处需要用户先用rsa私钥解密encrypt_random_key后,作为encrypt_key参数传入sdk来解密encrypt_chat_msg获取会话存档明文。
*/
int ret = Finance.DecryptData(sdk, decryptByPriKey, chatData.getEncryptChatMsg(), msg);
if (ret != 0) {
Finance.FreeSlice(msg);
Finance.DestroySingletonSDK(sdk);
throw new WxErrorException("msg err ret " + ret);
}

// 明文
/**
* 明文
*/
String plainText = Finance.GetContentFromSlice(msg);
Finance.FreeSlice(msg);
return plainText;
}

@Override
public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData) throws Exception {
return this.decryptChatData(chatData);
public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
return this.decryptChatData(chatData, pkcs1);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package me.chanjar.weixin.cp.api.impl;

import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpSchoolService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.school.WxCpCustomizeHealthInfo;
import me.chanjar.weixin.cp.bean.school.WxCpResultList;

import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.School.*;

/**
* 企业微信家校应用 复学码相关接口实现类.
* https://developer.work.weixin.qq.com/document/path/93744
*
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date: 2022/6/1 14:05
*/
@Slf4j
@RequiredArgsConstructor
public class WxCpSchoolServiceImpl implements WxCpSchoolService {

private final WxCpService cpService;

@Override
public WxCpCustomizeHealthInfo getTeacherCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_TEACHER_CUSTOMIZE_HEALTH_INFO);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("date", date);
jsonObject.addProperty("limit", Optional.ofNullable(limit).orElse(100));
if (nextKey != null) {
jsonObject.addProperty("next_key", nextKey);
}
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
return WxCpCustomizeHealthInfo.fromJson(responseContent);
}

@Override
public WxCpCustomizeHealthInfo getStudentCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_STUDENT_CUSTOMIZE_HEALTH_INFO);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("date", date);
jsonObject.addProperty("limit", Optional.ofNullable(limit).orElse(100));
if (nextKey != null) {
jsonObject.addProperty("next_key", nextKey);
}
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
return WxCpCustomizeHealthInfo.fromJson(responseContent);
}

@Override
public WxCpResultList getHealthQrCode(@NotNull List<String> userIds, @NotNull Integer type) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_HEALTH_QRCODE);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("type", type);
jsonObject.addProperty("userids", userIds.toString());
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
return WxCpResultList.fromJson(responseContent);
}

}
Loading