🆕 #1947 【企业微信】第三方应用增加用户管理、OA、外部联系人、部门、通讯录搜索等相关接口的实现

* 修复getSuiteJsApiTicket和getAuthCorpJsApiTicket方法代码错误
* 增加企业微信第三方应用用户管理、oa、外部联系人、部门、通讯录搜索相关接口实现
This commit is contained in:
uianz 2020-12-29 10:01:22 +08:00 committed by GitHub
parent e946ce0642
commit 0af608f119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1423 additions and 27 deletions

View File

@ -0,0 +1,62 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.experimental.Accessors;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* @author uianz
* @description
* @since 2020/12/23 下午 02:43
*/
@Data
@Accessors(chain = true)
public class WxCpTpContactSearch {
/**
* 查询的企业corpid
*/
@SerializedName("auth_corpid")
private String authCorpId;
/**
* 搜索关键词当查询用户时应为用户名称名称拼音或者英文名当查询部门时应为部门名称或者部门名称拼音
*/
@SerializedName("query_word")
private String queryWord;
/**
* 查询类型 1查询用户返回用户userid列表 2查询部门返回部门id列表 不填该字段或者填0代表同时查询部门跟用户
*/
@SerializedName("query_type")
private Integer type;
/**
* 应用id若非0则只返回应用可见范围内的用户或者部门信息
*/
@SerializedName("agentid")
private Integer agentId;
/**
* 查询的偏移量每次调用的offset在上一次offset基础上加上limit
*/
@SerializedName("offset")
private Integer offset;
/**
* 查询返回的最大数量默认为50最多为200查询返回的数量可能小于limit指定的值
*/
@SerializedName("limit")
private Integer limit;
/**
* 如果需要精确匹配用户名称或者部门名称或者英文名不填则默认为模糊匹配1匹配用户名称或者部门名称 2匹配用户英文名
*/
@SerializedName("full_match_field")
private Integer fullMatchField;
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,53 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* @author uianz
* @description
* @since 2020/12/23 下午 02:55
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class WxCpTpContactSearchResp extends WxCpBaseResp {
@SerializedName("is_last")
private Boolean isLast;
@SerializedName("query_result")
private QueryResult queryResult;
@Data
public static class QueryResult {
@SerializedName("user")
private User user;
@SerializedName("party")
private Party party;
@Data
public static class User {
@SerializedName("userid")
private List<String> userid;
@SerializedName("open_userid")
private List<String> openUserId;
}
@Data
public static class Party {
@SerializedName("department_id")
private List<Integer> departmentId;
}
}
public static WxCpTpContactSearchResp fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpTpContactSearchResp.class);
}
}

View File

@ -0,0 +1,31 @@
package me.chanjar.weixin.cp.bean;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
/**
* 企业微信的部门.
*
* @author Daniel Qian
*/
@Data
public class WxCpTpDepart implements Serializable {
private static final long serialVersionUID = -5028321625140879571L;
private Integer id;
private String name;
private String enName;
private Integer parentid;
private Integer order;
public static WxCpTpDepart fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpTpDepart.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,142 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* <pre>
* 外部联系人详情
* Created by Binary Wang on 2018/9/16.
* 参考文档https://work.weixin.qq.com/api/doc#13878
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Getter
@Setter
public class WxCpUserExternalContactInfo {
@SerializedName("external_contact")
private ExternalContact externalContact;
@SerializedName("follow_user")
private List<FollowedUser> followedUsers;
@Getter
@Setter
public static class ExternalContact {
@SerializedName("external_userid")
private String externalUserId;
@SerializedName("position")
private String position;
@SerializedName("name")
private String name;
@SerializedName("avatar")
private String avatar;
@SerializedName("corp_name")
private String corpName;
@SerializedName("corp_full_name")
private String corpFullName;
@SerializedName("type")
private Integer type;
@SerializedName("gender")
private Integer gender;
@SerializedName("unionid")
private String unionId;
@SerializedName("external_profile")
private ExternalProfile externalProfile;
}
@Setter
@Getter
public static class ExternalProfile {
@SerializedName("external_attr")
private List<ExternalAttribute> externalAttrs;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ExternalAttribute {
@Setter
@Getter
public static class Text {
private String value;
}
@Setter
@Getter
public static class Web {
private String title;
private String url;
}
@Setter
@Getter
public static class MiniProgram {
@SerializedName("pagepath")
private String pagePath;
private String appid;
private String title;
}
private int type;
private String name;
private Text text;
private Web web;
@SerializedName("miniprogram")
private MiniProgram miniProgram;
}
@Setter
@Getter
public static class FollowedUser {
@SerializedName("userid")
private String userId;
private String remark;
private String description;
@SerializedName("createtime")
private Long createTime;
private String state;
@SerializedName("remark_company")
private String remarkCompany;
@SerializedName("remark_mobiles")
private String[] remarkMobiles;
private Tag[] tags;
@SerializedName("add_way")
private Integer addWay;
@SerializedName("oper_userid")
private String operUserid;
}
public static WxCpUserExternalContactInfo fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class);
}
@Setter
@Getter
public static class Tag {
@SerializedName("group_name")
private String groupName;
@SerializedName("tag_name")
private String tagName;
private int type;
}
}

View File

@ -0,0 +1,65 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* @description: 登录信息
* @author: Jamie.shi
* @create: 2020-08-03 17:18
**/
@Data
public class WxTpLoginInfo extends WxCpBaseResp {
@SerializedName("usertype")
private Integer userType;
@SerializedName("user_info")
private UserInfo userInfo;
@SerializedName("corp_info")
private CorpInfoBean corpInfo;
@SerializedName("auth_info")
private AuthInfo authInfo;
private List<Agent> agent;
@Data
public static class UserInfo {
@SerializedName("userid")
private String userId;
@SerializedName("open_userid")
private String openUserId;
private String name;
private String avatar;
}
@Data
public static class CorpInfoBean {
@SerializedName("corpid")
private String corpId;
}
@Data
public static class AuthInfo {
private List<Department> department;
@Data
public static class Department {
private int id;
private boolean writable;
}
}
@Data
public static class Agent {
@SerializedName("agentid")
private int agentId;
@SerializedName("auth_type")
private int authType;
}
public static WxTpLoginInfo fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxTpLoginInfo.class);
}
}

View File

@ -62,6 +62,11 @@ public interface WxCpTpConfigStorage {
String getCorpId();
String getCorpSecret();
/**
* 服务商secret
*/
String getProviderSecret();
/**
* 授权企业的access token相关
*/
@ -83,6 +88,11 @@ public interface WxCpTpConfigStorage {
boolean isAuthSuiteJsApiTicketExpired(String authCorpId);
void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);;
boolean isProviderTokenExpired();
void updateProviderToken(String providerToken, int expiredInSeconds);
String getProviderToken();
/**
* 网络代理相关
*/

View File

@ -20,13 +20,19 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
private volatile String corpId;
private volatile String corpSecret;
/**
* 服务商secret
*/
private volatile String providerSecret;
private volatile String providerToken;
private volatile long providerTokenExpiresTime;
private volatile String suiteId;
private volatile String suiteSecret;
private volatile String token;
private volatile String suiteAccessToken;
private volatile long suiteAccessTokenExpiresTime;
private volatile long suiteAccessTokenExpiresTime;
private volatile String aesKey;
private volatile String suiteTicket;
@ -88,7 +94,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
@Override
public synchronized void updateSuiteAccessToken(WxAccessToken suiteAccessToken) {
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
}
@Override
@ -196,6 +202,11 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
return this.corpSecret;
}
@Override
public String getProviderSecret() {
return providerSecret;
}
@Deprecated
public void setCorpSecret(String corpSecret) {
this.corpSecret = corpSecret;
@ -230,8 +241,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
Long t = this.authCorpJsApiTicketExpireTimeMap.get(authCorpId);
if (t == null) {
return System.currentTimeMillis() > t;
}
else {
} else {
return true;
}
}
@ -254,8 +264,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
Long t = authSuiteJsApiTicketExpireTimeMap.get(authCorpId);
if (t == null) {
return System.currentTimeMillis() > t;
}
else {
} else {
return true;
}
}
@ -268,6 +277,22 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
authSuiteJsApiTicketExpireTimeMap.put(authCorpId, System.currentTimeMillis() + (expiredInSeconds - 200) * 1000L);
}
@Override
public boolean isProviderTokenExpired() {
return System.currentTimeMillis() > providerTokenExpiresTime;
}
@Override
public void updateProviderToken(String providerToken, int expiredInSeconds) {
this.providerToken = providerToken;
this.providerTokenExpiresTime = System.currentTimeMillis() + expiredInSeconds * 1000L;
}
@Override
public String getProviderToken() {
return providerToken;
}
public void setOauth2redirectUri(String oauth2redirectUri) {
this.oauth2redirectUri = oauth2redirectUri;
}

View File

@ -35,6 +35,8 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
private final String authSuiteJsApiTicketKey = ":authSuiteJsApiTicketKey:";
private final String providerTokenKey = ":providerTokenKey:";
private volatile String baseApiUrl;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
@ -59,6 +61,11 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
private volatile String corpId;
private volatile String corpSecret;
/**
* 服务商secret
*/
private volatile String providerSecret;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
@ -69,7 +76,8 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
if (baseApiUrl == null) {
baseApiUrl = "https://qyapi.weixin.qq.com";
}
return baseApiUrl + path; }
return baseApiUrl + path;
}
/**
@ -164,6 +172,10 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
return corpSecret;
}
@Override
public String getProviderSecret() {
return providerSecret;
}
/**
* 授权企业的access token相关
@ -203,7 +215,8 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
@Override
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, jsApiTicket, expiredInSeconds, TimeUnit.SECONDS);
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, jsApiTicket, expiredInSeconds,
TimeUnit.SECONDS);
}
@ -224,7 +237,24 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
@Override
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, jsApiTicket, expiredInSeconds, TimeUnit.SECONDS);
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, jsApiTicket, expiredInSeconds,
TimeUnit.SECONDS);
}
@Override
public boolean isProviderTokenExpired() {
//remain time to live in seconds, or key not exist
return wxRedisOps.getExpire(keyWithPrefix(providerTokenKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(providerTokenKey)) == -2;
}
@Override
public void updateProviderToken(String providerToken, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(providerTokenKey), providerToken, expiredInSeconds, TimeUnit.SECONDS);
}
@Override
public String getProviderToken() {
return wxRedisOps.getValue(keyWithPrefix(providerTokenKey));
}

View File

@ -119,6 +119,8 @@ public final class WxCpApiPathConsts {
public static final String SCHEDULE_GET = "/cgi-bin/oa/schedule/get";
public static final String SCHEDULE_DEL = "/cgi-bin/oa/schedule/del";
public static final String SCHEDULE_LIST = "/cgi-bin/oa/schedule/get_by_calendar";
public static final String COPY_TEMPLATE = "/cgi-bin/oa/approval/copytemplate";
}
@UtilityClass
@ -150,6 +152,10 @@ public final class WxCpApiPathConsts {
public static final String GET_SUITE_JSAPI_TICKET = "/cgi-bin/ticket/get";
public static final String GET_USERINFO3RD = "/cgi-bin/service/getuserinfo3rd";
public static final String GET_USERDETAIL3RD = "/cgi-bin/service/getuserdetail3rd";
public static final String GET_LOGIN_INFO = "/cgi-bin/service/get_login_info";
public static final String CONTACT_SEARCH = "/cgi-bin/service/contact/search";
}
@UtilityClass

View File

@ -0,0 +1,22 @@
package me.chanjar.weixin.cp.tp.service;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpTpContactSearch;
import me.chanjar.weixin.cp.bean.WxCpTpContactSearchResp;
/**
* @author uianz
* @description
* @since 2020/12/23 下午 02:39
*/
public interface WxCpTpContactService {
/**
* https://work.weixin.qq.com/api/doc/90001/90143/91844
* 通讯录单个搜索
* @param wxCpTpContactSearch
* @return
* @throws WxErrorException
*/
WxCpTpContactSearchResp contactSearch(WxCpTpContactSearch wxCpTpContactSearch) throws WxErrorException;
}

View File

@ -0,0 +1,75 @@
package me.chanjar.weixin.cp.tp.service;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpTpDepart;
import java.util.List;
/**
* <pre>
* 部门管理接口
* Created by jamie on 2020/7/22.
* </pre>
*/
public interface WxCpTpDepartmentService {
/**
* <pre>
* 部门管理接口 - 创建部门.
* 最多支持创建500个部门
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90205
* </pre>
*
* @param depart 部门
* @return 部门id
* @throws WxErrorException 异常
*/
Long create(WxCpTpDepart depart) throws WxErrorException;
/**
* <pre>
* 部门管理接口 - 获取部门列表.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90208
* </pre>
*
* @param id 部门id获取指定部门及其下的子部门非必需可为null
* @return 获取的部门列表
* @throws WxErrorException 异常
*/
List<WxCpTpDepart> list(Long id, String corpId) throws WxErrorException;
/**
* <pre>
* 部门管理接口 - 更新部门.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90206
* 如果id为0(未部门),1(黑名单),2(星标组)或者不存在的id微信会返回系统繁忙的错误
* </pre>
*
* @param group 要更新的groupgroup的id,name必须设置
* @throws WxErrorException 异常
*/
void update(WxCpTpDepart group) throws WxErrorException;
/**
* <pre>
* 部门管理接口 - 删除部门.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90207
* 应用须拥有指定部门的管理权限
* </pre>
*
* @param departId 部门id
* @throws WxErrorException 异常
*/
void delete(Long departId) throws WxErrorException;
/**
* <pre>
* 部门管理接口 - 获取部门列表.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90208
* </pre>
*
* @return 获取所有的部门列表
* @throws WxErrorException 异常
*/
List<WxCpTpDepart> list(String corpId) throws WxErrorException;
}

View File

@ -0,0 +1,60 @@
package me.chanjar.weixin.cp.tp.service;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* <pre>
* 媒体管理接口.
* Created by BinaryWang on 2017/6/24.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpTpMediaService {
/**
* <pre>
* 上传多媒体文件.
* 上传的多媒体文件有格式和大小限制如下
* 图片image: 1M支持JPG格式
* 语音voice2M播放长度不超过60s支持AMR\MP3格式
* 视频video10MB支持MP4格式
* 缩略图thumb64KB支持JPG格式
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
* </pre>
*
* @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts}
* @param fileType 文件类型请看{@link me.chanjar.weixin.common.api.WxConsts}
* @param inputStream 输入流需要调用方控制关闭该输入流
*/
WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream, String corpId)
throws WxErrorException, IOException;
/**
* 上传多媒体文件.
*
* @param mediaType 媒体类型
* @param file 文件对象
* @see #upload(String, String, InputStream, String)
*/
WxMediaUploadResult upload(String mediaType, File file, String corpId) throws WxErrorException;
/**
* <pre>
* 上传图片.
* 上传图片得到图片URL该URL永久有效
* 返回的图片URL仅能用于图文消息mpnews正文中的图片展示若用于非企业微信域名下的页面图片将被屏蔽
* 每个企业每天最多可上传100张图片
* 接口url格式https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN
* </pre>
*
* @param file 上传的文件对象
* @return 返回图片url
*/
String uploadImg(File file, String corpId) throws WxErrorException;
}

View File

@ -0,0 +1,60 @@
package me.chanjar.weixin.cp.tp.service;
import lombok.NonNull;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.oa.WxCpApprovalDetailResult;
import me.chanjar.weixin.cp.bean.oa.WxCpOaApplyEventRequest;
import me.chanjar.weixin.cp.bean.oa.WxCpTemplateResult;
/**
* 企业微信OA相关接口.
*
* @author Element
* @date 2019-04-06 10:52
*/
public interface WxCpTpOAService {
/**
* <pre>提交审批申请
* 调试工具
* 企业可通过审批应用或自建应用Secret调用本接口代应用可见范围内员工在企业微信审批应用内提交指定类型的审批申请
*
* 请求方式POSTHTTPS
* 请求地址 https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=ACCESS_TOKEN
* 文档地址https://work.weixin.qq.com/api/doc/90000/90135/91853
* </pre>
*
* @param request 请求
* @return 表单提交成功后返回的表单编号
* @throws WxErrorException .
*/
String apply(WxCpOaApplyEventRequest request, String corpId) throws WxErrorException;
/**
* 获取审批模板详情
*
* @param templateId 模板ID
* @return .
* @throws WxErrorException .
*/
WxCpTemplateResult getTemplateDetail(@NonNull String templateId, String corpId) throws WxErrorException;
/**
* 复制/更新模板到企业
*
* @param openTemplateId 模板ID
* @return .
* @throws WxErrorException .
*/
String copyTemplate(@NonNull String openTemplateId, String corpId) throws WxErrorException;
/**
* <pre>
* 获取审批申请详情
*
* @param spNo 审批单编号
* @return WxCpApprovaldetail
* @throws WxErrorException .
*/
WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo, String corpId) throws WxErrorException;
}

View File

@ -312,4 +312,79 @@ public interface WxCpTpService {
* @return
*/
WxCpTpUserDetail getUserDetail3rd(String userTicket) throws WxErrorException;
/**
* 企业用户登录信息
* @param authCode
* @return
* @throws WxErrorException
*/
WxTpLoginInfo getLoginInfo(String authCode) throws WxErrorException;
/**
* 获取服务商providerToken
* @return
* @throws WxErrorException
*/
String getWxCpProviderToken() throws WxErrorException;
/**
* get contact service
* @return WxCpTpContactService
*/
WxCpTpContactService getWxCpTpContactService();
/**
* get department service
* @return WxCpTpDepartmentService
*/
WxCpTpDepartmentService getWxCpTpDepartmentService();
/**
* get media service
* @return WxCpTpMediaService
*/
WxCpTpMediaService getWxCpTpMediaService();
/**
* get oa service
* @return WxCpTpOAService
*/
WxCpTpOAService getWxCpTpOAService();
/**
* get user service
* @return WxCpTpUserService
*/
WxCpTpUserService getWxCpTpUserService();
/**
* set contact service
* @param wxCpTpContactService the contact service
*/
void setWxCpTpContactService(WxCpTpContactService wxCpTpContactService);
/**
* set department service
* @param wxCpTpDepartmentService the department service
*/
void setWxCpTpDepartmentService(WxCpTpDepartmentService wxCpTpDepartmentService);
/**
* set media service
* @param wxCpTpMediaService the media service
*/
void setWxCpTpMediaService(WxCpTpMediaService wxCpTpMediaService);
/**
* set oa service
* @param wxCpTpOAService the oa service
*/
void setWxCpTpOAService(WxCpTpOAService wxCpTpOAService);
/**
* set user service
* @param wxCpTpUserService the set user service
*/
void setWxCpTpUserService(WxCpTpUserService wxCpTpUserService);
}

View File

@ -0,0 +1,172 @@
package me.chanjar.weixin.cp.tp.service;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpInviteResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;
import java.util.List;
import java.util.Map;
/**
* <pre>
* 用户管理接口
* Created by jamie on 2020/7/22.
* </pre>
*
*/
public interface WxCpTpUserService {
/**
* <pre>
* 用在二次验证的时候.
* 企业在员工验证成功后调用本方法告诉企业号平台该员工关注成功
* </pre>
*
* @param userId 用户id
*/
void authenticate(String userId) throws WxErrorException;
/**
* <pre>
* 获取部门成员(详情).
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29
* </pre>
*
* @param departId 必填部门id
* @param fetchChild 非必填1/0是否递归获取子部门下面的成员
* @param status 非必填0获取全部员工1获取已关注成员列表2获取禁用成员列表4获取未关注成员列表status可叠加
*/
List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status,String corpId) throws WxErrorException;
/**
* <pre>
* 获取部门成员.
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98
* </pre>
*
* @param departId 必填部门id
* @param fetchChild 非必填1/0是否递归获取子部门下面的成员
* @param status 非必填0获取全部员工1获取已关注成员列表2获取禁用成员列表4获取未关注成员列表status可叠加
*/
List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException;
/**
* 新建用户.
*
* @param user 用户对象
*/
void create(WxCpUser user) throws WxErrorException;
/**
* 更新用户.
*
* @param user 用户对象
*/
void update(WxCpUser user) throws WxErrorException;
/**
* <pre>
* 删除用户/批量删除成员.
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98
* </pre>
*
* @param userIds 员工UserID列表对应管理端的帐号
*/
void delete(String... userIds) throws WxErrorException;
/**
* 获取用户.
*
* @param userid 用户id
*/
WxCpUser getById(String userid,String corpId) throws WxErrorException;
/**
* <pre>
* 邀请成员.
* 企业可通过接口批量邀请成员使用企业微信邀请后将通过短信或邮件下发通知
* 请求方式POSTHTTPS
* 请求地址 https://qyapi.weixin.qq.com/cgi-bin/batch/invite?access_token=ACCESS_TOKEN
* 文档地址https://work.weixin.qq.com/api/doc#12543
* </pre>
*
* @param userIds 成员ID列表, 最多支持1000个
* @param partyIds 部门ID列表最多支持100个
* @param tagIds 标签ID列表最多支持100个
*/
WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds) throws WxErrorException;
/**
* <pre>
* userid转openid.
* 该接口使用场景为微信支付微信红包和企业转账
*
* 在使用微信支付的功能时需要自行将企业微信的userid转成openid
* 在使用微信红包功能时需要将应用id和userid转成appid和openid才能使用
* 需要成员使用微信登录企业微信或者关注微信插件才能转成openid
*
* 文档地址https://work.weixin.qq.com/api/doc#11279
* </pre>
*
* @param userId 企业内的成员id
* @param agentId 非必填整型仅用于发红包其它场景该参数不要填如微信支付企业转账电子发票
* @return map对象可能包含以下值
* - openid 企业微信成员userid对应的openid若有传参agentid则是针对该agentid的openid否则是针对企业微信corpid的openid
* - appid 应用的appid若请求包中不包含agentid则不返回appid该appid在使用微信红包时会用到
*/
Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException;
/**
* <pre>
* openid转userid.
*
* 该接口主要应用于使用微信支付微信红包和企业转账之后的结果查询
* 开发者需要知道某个结果事件的openid对应企业微信内成员的信息时可以通过调用该接口进行转换查询
* 权限说明
* 管理组需对openid对应的企业微信成员有查看权限
*
* 文档地址https://work.weixin.qq.com/api/doc#11279
* </pre>
*
* @param openid 在使用微信支付微信红包和企业转账之后返回结果的openid
* @return userid 该openid在企业微信对应的成员userid
*/
String openid2UserId(String openid) throws WxErrorException;
/**
* <pre>
*
* 通过手机号获取其所对应的userid
*
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN
*
* 文档地址https://work.weixin.qq.com/api/doc#90001/90143/91693
* </pre>
*
* @param mobile 手机号码长度为5~32个字节
* @return userid mobile对应的成员userid
* @throws WxErrorException .
*/
String getUserId(String mobile) throws WxErrorException;
/**
* 获取外部联系人详情.
* <pre>
* 企业可通过此接口根据外部联系人的userid拉取外部联系人详情权限说明
* 企业需要使用外部联系人管理secret所获取的accesstoken来调用
* 第三方应用需拥有企业客户权限
* 第三方应用调用时返回的跟进人follow_user仅包含应用可见范围之内的成员
* </pre>
*
* @param userId 外部联系人的userid
* @return 联系人详情
* @throws WxErrorException .
*/
WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException;
}

View File

@ -22,7 +22,7 @@ import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.bean.*;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.tp.service.*;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
@ -41,6 +41,12 @@ import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.*;
@Slf4j
public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, RequestHttp<H, P> {
private WxCpTpContactService wxCpTpContactService = new WxCpTpContactServiceImpl(this);
private WxCpTpDepartmentService wxCpTpDepartmentService = new WxCpTpDepartmentServiceImpl(this);
private WxCpTpMediaService wxCpTpMediaService = new WxCpTpMediaServiceImpl(this);
private WxCpTpOAService wxCpTpOAService = new WxCpTpOAServiceImpl(this);
private WxCpTpUserService wxCpTpUserService = new WxCpTpUserServiceImpl(this);
/**
* 全局的是否正在刷新access token的锁.
*/
@ -57,6 +63,13 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
*/
protected final Object globalJsApiTicketRefreshLock = new Object();
/**
* 全局的是否正在刷新auth_corp_jsapi_ticket的锁.
*/
protected final Object globalAuthCorpJsApiTicketRefreshLock = new Object();
protected final Object globalProviderTokenRefreshLock = new Object();
protected WxCpTpConfigStorage configStorage;
private WxSessionManager sessionManager = new StandardSessionManager();
@ -104,12 +117,12 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
}
@Override
public void setSuiteTicket(String suiteTicket){
public void setSuiteTicket(String suiteTicket) {
setSuiteTicket(suiteTicket, 28 * 60);
}
@Override
public void setSuiteTicket(String suiteTicket, int expiresInSeconds){
public void setSuiteTicket(String suiteTicket, int expiresInSeconds) {
synchronized (globalSuiteTicketRefreshLock) {
this.configStorage.updateSuiteTicket(suiteTicket, expiresInSeconds);
}
@ -117,7 +130,7 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
@Override
public String getSuiteJsApiTicket(String authCorpId) throws WxErrorException {
if (this.configStorage.isSuiteAccessTokenExpired()) {
if (this.configStorage.isAuthSuiteJsApiTicketExpired(authCorpId)) {
String resp = get(configStorage.getApiUrl(GET_SUITE_JSAPI_TICKET),
"type=agent_config&access_token=" + this.configStorage.getAccessToken(authCorpId));
@ -129,18 +142,17 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
synchronized (globalJsApiTicketRefreshLock) {
configStorage.updateAuthSuiteJsApiTicket(authCorpId, jsApiTicket, expiredInSeconds);
}
}
else {
} else {
throw new WxErrorException(WxError.fromJson(resp));
}
}
return configStorage.getSuiteAccessToken();
return configStorage.getAuthSuiteJsApiTicket(authCorpId);
}
@Override
public String getAuthCorpJsApiTicket(String authCorpId) throws WxErrorException {
if (this.configStorage.isSuiteAccessTokenExpired()) {
if (this.configStorage.isAuthCorpJsApiTicketExpired(authCorpId)) {
String resp = get(configStorage.getApiUrl(GET_AUTH_CORP_JSAPI_TICKET),
"access_token=" + this.configStorage.getAccessToken(authCorpId));
@ -150,16 +162,14 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
String jsApiTicket = jsonObject.get("ticket").getAsString();
int expiredInSeconds = jsonObject.get("expires_in").getAsInt();
synchronized (globalJsApiTicketRefreshLock) {
synchronized (globalAuthCorpJsApiTicketRefreshLock) {
configStorage.updateAuthCorpJsApiTicket(authCorpId, jsApiTicket, expiredInSeconds);
}
}
else {
} else {
throw new WxErrorException(WxError.fromJson(resp));
}
}
return configStorage.getSuiteAccessToken();
return configStorage.getProviderToken();
}
@Override
@ -223,9 +233,9 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
WxCpTpPreauthCode preAuthCode = WxCpTpPreauthCode.fromJson(result);
String setSessionUrl = "https://qyapi.weixin.qq.com/cgi-bin/service/set_session_info";
Map<String,Object> sessionInfo = new HashMap<>(1);
Map<String, Object> sessionInfo = new HashMap<>(1);
sessionInfo.put("auth_type", authType);
Map<String,Object> param = new HashMap<>(2);
Map<String, Object> param = new HashMap<>(2);
param.put("pre_auth_code", preAuthCode.getPreAuthCode());
param.put("session_info", sessionInfo);
String postData = new Gson().toJson(param);
@ -373,18 +383,97 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
}
@Override
public WxCpTpUserInfo getUserInfo3rd(String code) throws WxErrorException{
public WxCpTpUserInfo getUserInfo3rd(String code) throws WxErrorException {
String url = configStorage.getApiUrl(GET_USERINFO3RD);
String result = get(url+"?code="+code,null);
String result = get(url + "?code=" + code, null);
return WxCpTpUserInfo.fromJson(result);
}
@Override
public WxCpTpUserDetail getUserDetail3rd(String userTicket) throws WxErrorException{
public WxCpTpUserDetail getUserDetail3rd(String userTicket) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("user_ticket", userTicket);
String result = post(configStorage.getApiUrl(GET_USERDETAIL3RD), jsonObject.toString());
return WxCpTpUserDetail.fromJson(result);
}
@Override
public WxTpLoginInfo getLoginInfo(String authCode) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("auth_code", authCode);
jsonObject.addProperty("access_token", configStorage.getAccessToken(authCode));
String responseText = post(configStorage.getApiUrl(GET_LOGIN_INFO), jsonObject.toString());
return WxTpLoginInfo.fromJson(responseText);
}
@Override
public String getWxCpProviderToken() throws WxErrorException {
if (this.configStorage.isProviderTokenExpired()) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("corpid", configStorage.getCorpId());
jsonObject.addProperty("provider_secret", configStorage.getProviderSecret());
WxCpProviderToken wxCpProviderToken =
WxCpProviderToken.fromJson(this.post(this.configStorage.getApiUrl(GET_PROVIDER_TOKEN)
, jsonObject.toString()));
String providerAccessToken = wxCpProviderToken.getProviderAccessToken();
Integer expiresIn = wxCpProviderToken.getExpiresIn();
synchronized (globalProviderTokenRefreshLock) {
configStorage.updateProviderToken(providerAccessToken, expiresIn - 200);
}
}
return configStorage.getProviderToken();
}
@Override
public WxCpTpContactService getWxCpTpContactService() {
return wxCpTpContactService;
}
@Override
public WxCpTpDepartmentService getWxCpTpDepartmentService(){
return wxCpTpDepartmentService;
}
@Override
public WxCpTpMediaService getWxCpTpMediaService(){
return wxCpTpMediaService;
}
@Override
public WxCpTpOAService getWxCpTpOAService(){
return wxCpTpOAService;
}
@Override
public WxCpTpUserService getWxCpTpUserService(){
return wxCpTpUserService;
}
@Override
public void setWxCpTpContactService(WxCpTpContactService wxCpTpContactService) {
this.wxCpTpContactService = wxCpTpContactService;
}
@Override
public void setWxCpTpDepartmentService(WxCpTpDepartmentService wxCpTpDepartmentService) {
this.wxCpTpDepartmentService = wxCpTpDepartmentService;
}
@Override
public void setWxCpTpMediaService(WxCpTpMediaService wxCpTpMediaService) {
this.wxCpTpMediaService = wxCpTpMediaService;
}
@Override
public void setWxCpTpOAService(WxCpTpOAService wxCpTpOAService) {
this.wxCpTpOAService = wxCpTpOAService;
}
@Override
public void setWxCpTpUserService(WxCpTpUserService wxCpTpUserService) {
this.wxCpTpUserService = wxCpTpUserService;
}
}

View File

@ -0,0 +1,29 @@
package me.chanjar.weixin.cp.tp.service.impl;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpTpContactSearch;
import me.chanjar.weixin.cp.bean.WxCpTpContactSearchResp;
import me.chanjar.weixin.cp.tp.service.WxCpTpContactService;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.CONTACT_SEARCH;
/**
* @author uianz
* @description
* @since 2020/12/23 下午 02:39
*/
@RequiredArgsConstructor
public class WxCpTpContactServiceImpl implements WxCpTpContactService {
private final WxCpTpService mainService;
@Override
public WxCpTpContactSearchResp contactSearch(WxCpTpContactSearch wxCpTpContactSearch) throws WxErrorException {
String responseText =
mainService.post(mainService.getWxCpTpConfigStorage().getApiUrl(CONTACT_SEARCH) + "?provider_access_token=" + mainService.getWxCpTpConfigStorage().getCorpSecret(), wxCpTpContactSearch.toJson());
return WxCpTpContactSearchResp.fromJson(responseText);
}
}

View File

@ -0,0 +1,70 @@
package me.chanjar.weixin.cp.tp.service.impl;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.impl.WxCpDepartmentServiceImpl;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import me.chanjar.weixin.cp.bean.WxCpTpDepart;
import me.chanjar.weixin.cp.tp.service.WxCpTpDepartmentService;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Department.*;
/**
* @author uianz
* @description corp from {@link WxCpDepartmentServiceImpl )}
* 唯一不同在于获取部门列表时需要传对应企业的accessToken
* @since 2020/12/23 下午 02:39
*/
@RequiredArgsConstructor
public class WxCpTpDepartmentServiceImpl implements WxCpTpDepartmentService {
private final WxCpTpService mainService;
@Override
public Long create(WxCpTpDepart depart) throws WxErrorException {
String url = this.mainService.getWxCpTpConfigStorage().getApiUrl(DEPARTMENT_CREATE);
String responseContent = this.mainService.post(url, depart.toJson());
JsonObject tmpJsonObject = GsonParser.parse(responseContent);
return GsonHelper.getAsLong(tmpJsonObject.get("id"));
}
@Override
public void update(WxCpTpDepart group) throws WxErrorException {
String url = this.mainService.getWxCpTpConfigStorage().getApiUrl(DEPARTMENT_UPDATE);
this.mainService.post(url, group.toJson());
}
@Override
public void delete(Long departId) throws WxErrorException {
String url = String.format(this.mainService.getWxCpTpConfigStorage().getApiUrl(DEPARTMENT_DELETE), departId);
this.mainService.get(url, null);
}
@Override
public List<WxCpTpDepart> list(Long id, String corpId) throws WxErrorException {
String url = this.mainService.getWxCpTpConfigStorage().getApiUrl(DEPARTMENT_LIST);
url += "?access_token=" + this.mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
if (id != null) {
url += "&id=" + id;
}
String responseContent = this.mainService.get(url, null);
JsonObject tmpJsonObject = GsonParser.parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(tmpJsonObject.get("department"),
new TypeToken<List<WxCpDepart>>() {
}.getType()
);
}
@Override
public List<WxCpTpDepart> list(String corpId) throws WxErrorException {
return list(null, corpId);
}
}

View File

@ -0,0 +1,50 @@
package me.chanjar.weixin.cp.tp.service.impl;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.cp.tp.service.WxCpTpMediaService;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.IMG_UPLOAD;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.MEDIA_UPLOAD;
/**
* <pre>
* 媒体管理接口.
* Created by Binary Wang on 2017-6-25.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpTpMediaServiceImpl implements WxCpTpMediaService {
private final WxCpTpService mainService;
@Override
public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream, String corpId)
throws WxErrorException, IOException {
return this.upload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType), corpId);
}
@Override
public WxMediaUploadResult upload(String mediaType, File file, String corpId) throws WxErrorException {
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()),
mainService.getWxCpTpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType) + "&access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId), file);
}
@Override
public String uploadImg(File file, String corpId) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(IMG_UPLOAD);
url += "&access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file)
.getUrl();
}
}

View File

@ -0,0 +1,66 @@
package me.chanjar.weixin.cp.tp.service.impl;
import com.google.gson.JsonObject;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.bean.oa.WxCpApprovalDetailResult;
import me.chanjar.weixin.cp.bean.oa.WxCpOaApplyEventRequest;
import me.chanjar.weixin.cp.bean.oa.WxCpTemplateResult;
import me.chanjar.weixin.cp.tp.service.WxCpTpOAService;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*;
/**
* 企业微信 OA 接口实现
*
* @author Element
* @date 2019-04-06 11:20
*/
@RequiredArgsConstructor
public class WxCpTpOAServiceImpl implements WxCpTpOAService {
private final WxCpTpService mainService;
@Override
public String apply(WxCpOaApplyEventRequest request, String corpId) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(APPLY_EVENT) +
"?access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String responseContent = this.mainService.post(url, request.toJson());
return GsonParser.parse(responseContent).get("sp_no").getAsString();
}
@Override
public WxCpTemplateResult getTemplateDetail(@NonNull String templateId, String corpId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("template_id", templateId);
String url = mainService.getWxCpTpConfigStorage().getApiUrl(GET_TEMPLATE_DETAIL) +
"?access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String responseContent = this.mainService.post(url, jsonObject.toString());
return WxCpGsonBuilder.create().fromJson(responseContent, WxCpTemplateResult.class);
}
@Override
public String copyTemplate(@NonNull String openTemplateId, String corpId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("open_template_id", openTemplateId);
String url = mainService.getWxCpTpConfigStorage().getApiUrl(COPY_TEMPLATE) +
"?access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String responseContent = this.mainService.post(url, jsonObject.toString());
return GsonParser.parse(responseContent).get("template_id").getAsString();
}
@Override
public WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo, String corpId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("sp_no", spNo);
final String url = mainService.getWxCpTpConfigStorage().getApiUrl(GET_APPROVAL_DETAIL) +
"?access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String responseContent = this.mainService.post(url, jsonObject.toString());
return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDetailResult.class);
}
}

View File

@ -0,0 +1,204 @@
package me.chanjar.weixin.cp.tp.service.impl;
import com.google.common.collect.Maps;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.bean.WxCpInviteResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.tp.service.WxCpTpUserService;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
import java.util.Map;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.User.*;
/**
* <pre>
* Created by jamie on 2020/7/22.
* </pre>
*/
@RequiredArgsConstructor
public class WxCpTpUserServiceImpl implements WxCpTpUserService {
private final WxCpTpService mainService;
@Override
public void authenticate(String userId) throws WxErrorException {
this.mainService.get(mainService.getWxCpTpConfigStorage().getApiUrl(USER_AUTHENTICATE + userId), null);
}
@Override
public void create(WxCpUser user) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_CREATE);
this.mainService.post(url, user.toJson());
}
@Override
public void update(WxCpUser user) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_UPDATE);
this.mainService.post(url, user.toJson());
}
@Override
public void delete(String... userIds) throws WxErrorException {
if (userIds.length == 1) {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_DELETE + userIds[0]);
this.mainService.get(url, null);
return;
}
JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();
for (String userId : userIds) {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("useridlist", jsonArray);
this.mainService.post(mainService.getWxCpTpConfigStorage().getApiUrl(USER_BATCH_DELETE),
jsonObject.toString());
}
@Override
public WxCpUser getById(String userid, String corpId) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_GET + userid);
url += "&access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String responseContent = this.mainService.get(url, null);
return WxCpUser.fromJson(responseContent);
}
@Override
public List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status, String corpId) throws WxErrorException {
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
}
if (status != null) {
params += "&status=" + status;
} else {
params += "&status=0";
}
params += "&access_token=" + mainService.getWxCpTpConfigStorage().getAccessToken(corpId);
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonObject tmpJsonElement = GsonParser.parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(tmpJsonElement.getAsJsonObject().get("userlist"),
new TypeToken<List<WxCpUser>>() {
}.getType()
);
}
@Override
public List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status)
throws WxErrorException {
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
}
if (status != null) {
params += "&status=" + status;
} else {
params += "&status=0";
}
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonObject tmpJsonElement = GsonParser.parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("userlist"),
new TypeToken<List<WxCpUser>>() {
}.getType()
);
}
@Override
public WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds)
throws WxErrorException {
JsonObject jsonObject = new JsonObject();
if (userIds != null) {
JsonArray jsonArray = new JsonArray();
for (String userId : userIds) {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("user", jsonArray);
}
if (partyIds != null) {
JsonArray jsonArray = new JsonArray();
for (String userId : partyIds) {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("party", jsonArray);
}
if (tagIds != null) {
JsonArray jsonArray = new JsonArray();
for (String tagId : tagIds) {
jsonArray.add(new JsonPrimitive(tagId));
}
jsonObject.add("tag", jsonArray);
}
String url = mainService.getWxCpTpConfigStorage().getApiUrl(BATCH_INVITE);
return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString()));
}
@Override
public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_CONVERT_TO_OPENID);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userid", userId);
if (agentId != null) {
jsonObject.addProperty("agentid", agentId);
}
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonObject tmpJsonElement = GsonParser.parse(responseContent);
Map<String, String> result = Maps.newHashMap();
if (tmpJsonElement.getAsJsonObject().get("openid") != null) {
result.put("openid", tmpJsonElement.getAsJsonObject().get("openid").getAsString());
}
if (tmpJsonElement.getAsJsonObject().get("appid") != null) {
result.put("appid", tmpJsonElement.getAsJsonObject().get("appid").getAsString());
}
return result;
}
@Override
public String openid2UserId(String openid) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("openid", openid);
String url = mainService.getWxCpTpConfigStorage().getApiUrl(USER_CONVERT_TO_USERID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonObject tmpJsonElement = GsonParser.parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
}
@Override
public String getUserId(String mobile) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("mobile", mobile);
String url = mainService.getWxCpTpConfigStorage().getApiUrl(GET_USER_ID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonObject tmpJsonElement = GsonParser.parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
}
@Override
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException {
String url = mainService.getWxCpTpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId);
String responseContent = this.mainService.get(url, null);
return WxCpUserExternalContactInfo.fromJson(responseContent);
}
}