475 lines
17 KiB
C#
475 lines
17 KiB
C#
using MyCode.Project.Domain.Message.Response.EnterpriseWechat;
|
||
using MyCode.Project.Domain.Message.Response.Wechat;
|
||
using MyCode.Project.Infrastructure.Cache;
|
||
using MyCode.Project.Infrastructure.Common;
|
||
using MyCode.Project.Infrastructure.Constant;
|
||
using MyCode.Project.Infrastructure.Exceptions;
|
||
using MyCode.Project.Infrastructure.WebPost;
|
||
using Senparc.CO2NET.Extensions;
|
||
using Senparc.Weixin;
|
||
using Senparc.Weixin.MP.Entities;
|
||
using Senparc.Weixin.MP.Helpers;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Text;
|
||
using System.Web;
|
||
|
||
namespace MyCode.Project.OutSideService.Implementation
|
||
{
|
||
public class EnterpriseWechatService : IEnterpriseWechatService
|
||
{
|
||
private readonly string apiUrl = "https://qyapi.weixin.qq.com/";
|
||
|
||
|
||
private readonly IMyCodeCacheService _myCodeCacheService;
|
||
|
||
public EnterpriseWechatService(
|
||
IMyCodeCacheService myCodeCacheService
|
||
)
|
||
{
|
||
_myCodeCacheService = myCodeCacheService;
|
||
|
||
}
|
||
|
||
#region 基础辅助
|
||
|
||
#region Get(进一步封装Get方法)
|
||
/// <summary>
|
||
/// 进一步封装Get方法
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="url"></param>
|
||
/// <returns></returns>
|
||
private T Get<T>(string url) where T : BaseEnterpriseResp
|
||
{
|
||
var webUtils = new WebUtils();
|
||
|
||
var resp = webUtils.DoGet<T>(url, null);
|
||
|
||
resp.CheckResult();
|
||
|
||
return resp;
|
||
}
|
||
#endregion
|
||
|
||
#region ToDepartmentHasChild(部门转成含有子节点的结构)
|
||
public List<DepartmentHasChild> ToDepartmentHasChild(List<DepartmentInfo> listDepartmentInfo)
|
||
{
|
||
if (listDepartmentInfo == null || listDepartmentInfo.Count == 0) { return null; }
|
||
|
||
//公司节点
|
||
var rootList = listDepartmentInfo.FindAll(p => p.Parentid == 0);
|
||
|
||
var returnList = new List<DepartmentHasChild>();
|
||
|
||
//子节点
|
||
foreach (var root in rootList)
|
||
{
|
||
var item = new DepartmentHasChild
|
||
{
|
||
Id = root.Id,
|
||
Name = root.Name,
|
||
Parentid = root.Parentid
|
||
};
|
||
|
||
item.ListChild = GetDepartmentChild(listDepartmentInfo, root.Id);
|
||
|
||
returnList.Add(item);
|
||
}
|
||
|
||
return returnList;
|
||
}
|
||
#endregion
|
||
|
||
#region GetDepartmentChild(得到部门子节点)
|
||
/// <summary>
|
||
/// 得到子节点
|
||
/// </summary>
|
||
/// <param name="listDepartmentInfo"></param>
|
||
/// <param name="parentId"></param>
|
||
/// <returns></returns>
|
||
public List<DepartmentHasChild> GetDepartmentChild(List<DepartmentInfo> listDepartmentInfo, int parentId)
|
||
{
|
||
var list = listDepartmentInfo.FindAll(p => p.Parentid == parentId);
|
||
|
||
var returnList = new List<DepartmentHasChild>();
|
||
|
||
if (list == null || list.Count == 0) { return new List<DepartmentHasChild> { }; }
|
||
|
||
foreach (var child in list)
|
||
{
|
||
var item = new DepartmentHasChild
|
||
{
|
||
Id = child.Id,
|
||
Name = child.Name,
|
||
Parentid = child.Parentid,
|
||
Order = child.Order
|
||
};
|
||
item.ListChild = GetDepartmentChild(listDepartmentInfo, child.Id);
|
||
|
||
returnList.Add(item);
|
||
|
||
}
|
||
|
||
|
||
return returnList;
|
||
}
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
#region GetAccessToken(得到AccessToken,有效期7200秒)
|
||
/// <summary>
|
||
/// 得到AccessToken,有效期7200秒
|
||
/// </summary>
|
||
/// <param name="corpId">企业id</param>
|
||
/// <param name="secret"></param>
|
||
/// <returns></returns>
|
||
public string GetAccessToken(string corpId, string secret)
|
||
{
|
||
var cacheKey = $"{CacheKey.EnterpriseWechatTokenKey}{corpId}{secret}";
|
||
|
||
if (_myCodeCacheService.Exists(cacheKey))
|
||
{
|
||
return _myCodeCacheService.Get<string>(cacheKey);
|
||
}
|
||
var url = $"{apiUrl}cgi-bin/gettoken?corpid={corpId}&corpsecret={secret}";
|
||
|
||
var resp = Get<GetTokenResp>(url);
|
||
|
||
var token = resp.Access_token;
|
||
|
||
_myCodeCacheService.Set(cacheKey, token, new TimeSpan(1, 0, 0));
|
||
|
||
return token;
|
||
}
|
||
#endregion
|
||
|
||
#region GetTicket(获取企业微信Ticket)
|
||
/// <summary>
|
||
/// 获取企业微信Ticket
|
||
/// </summary>
|
||
/// <param name="accessToken">对应的应用的token</param>
|
||
/// <returns></returns>
|
||
private string GetTicket(string accessToken)
|
||
{
|
||
var cacheKey = $"{accessToken}";
|
||
|
||
if (_myCodeCacheService.Exists(cacheKey))
|
||
{
|
||
return _myCodeCacheService.Get<string>(cacheKey);
|
||
}
|
||
|
||
var url = string.Format(apiUrl + "cgi-bin/get_jsapi_ticket?access_token={0}", accessToken.AsUrlData());
|
||
|
||
JsApiTicketResult result = Senparc.CO2NET.HttpUtility.Get.GetJson<JsApiTicketResult>(url);
|
||
|
||
if (result.errcode != ReturnCode.请求成功) { throw new BaseException(result.ToJson()); }
|
||
|
||
_myCodeCacheService.Set(cacheKey, result.ticket, new TimeSpan(1, 0, 0));
|
||
|
||
|
||
return result.ticket;
|
||
}
|
||
#endregion
|
||
|
||
#region GetApplicationTicket(应用的jsapi_ticket)
|
||
/// <summary>
|
||
/// 应用的jsapi_ticket
|
||
/// </summary>
|
||
/// <param name="accessToken">对应的应用的token</param>
|
||
/// <returns></returns>
|
||
private string GetApplicationTicket(string accessToken)
|
||
{
|
||
var cacheKey = $"{accessToken}2";
|
||
|
||
if (_myCodeCacheService.Exists(cacheKey))
|
||
{
|
||
return _myCodeCacheService.Get<string>(cacheKey);
|
||
}
|
||
|
||
var url = string.Format(apiUrl + "cgi-bin/ticket/get?access_token={0}&type=agent_config", accessToken.AsUrlData());
|
||
|
||
JsApiTicketResult result = Senparc.CO2NET.HttpUtility.Get.GetJson<JsApiTicketResult>(url);
|
||
|
||
if (result.errcode != ReturnCode.请求成功) { throw new BaseException(result.ToJson()); }
|
||
|
||
_myCodeCacheService.Set(cacheKey, result.ticket, new TimeSpan(1, 0, 0));
|
||
|
||
|
||
return result.ticket;
|
||
}
|
||
#endregion
|
||
|
||
#region GetJsSdk(取得企业微信JsSdk相关参数值)
|
||
/// <summary>
|
||
/// 取得微信JsSdk相关参数值
|
||
/// </summary>
|
||
/// <param name="appId">微信应用ID</param>
|
||
/// <param name="ticket">JsTicket</param>
|
||
/// <param name="url">需要授权Url地址</param>
|
||
/// <returns></returns>
|
||
private JsSdkResp GetJsSdk(string appId, string ticket, string url)
|
||
{
|
||
var decodeUrl = HttpUtility.UrlDecode(url);
|
||
string timeStamp = JSSDKHelper.GetTimestamp();
|
||
string nonceStr = JSSDKHelper.GetNoncestr();
|
||
|
||
string signature = "";
|
||
var paySignReqHandler = new Senparc.Weixin.TenPay.V2.RequestHandler(null);
|
||
|
||
paySignReqHandler.SetParameter("jsapi_ticket", ticket);
|
||
paySignReqHandler.SetParameter("noncestr", nonceStr);
|
||
paySignReqHandler.SetParameter("timestamp", timeStamp);
|
||
paySignReqHandler.SetParameter("url", decodeUrl);
|
||
|
||
signature = paySignReqHandler.CreateSHA1Sign();
|
||
|
||
return new JsSdkResp
|
||
{
|
||
AppId = appId,
|
||
Timestamp = timeStamp,
|
||
NonceStr = nonceStr,
|
||
Signature = signature
|
||
};
|
||
}
|
||
#endregion
|
||
|
||
#region 获取企业微信的JS-SDK权限验证的签名Signature
|
||
/// <summary>
|
||
/// 获取企业微信的JS-SDK权限验证的签名Signature
|
||
/// </summary>
|
||
/// <param name="jsapi_ticket">jsapi_ticket</param>
|
||
/// <param name="noncestr">随机字符串(必须与wx.config中的nonceStr相同)</param>
|
||
/// <param name="timestamp">时间戳(必须与wx.config中的timestamp相同)</param>
|
||
/// <param name="url">当前网页的URL,不包含#及其后面部分(必须是调用JS接口页面的完整URL)</param>
|
||
/// <returns></returns>
|
||
private string GetSignature(string jsapi_ticket, string noncestr, long timestamp, string url)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
sb.Append("jsapi_ticket=").Append(jsapi_ticket).Append("&")
|
||
.Append("noncestr=").Append(noncestr).Append("&")
|
||
.Append("timestamp=").Append(timestamp).Append("&")
|
||
.Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0, url.IndexOf("#")) : url);
|
||
string dddd = sb.ToString();
|
||
LogHelper.Info("Signature: " + dddd);
|
||
return Senparc.CO2NET.Helpers.EncryptHelper.GetSha1(sb.ToString()).ToLower();
|
||
}
|
||
#endregion
|
||
|
||
//#region GetJsSdk(取得企业微信某个应用的JsSdk相关参数值)
|
||
///// <summary>
|
||
///// 取得企业微信某个应用的JsSdk相关参数值
|
||
///// </summary>
|
||
///// <param name="url"></param>
|
||
///// <param name="tokenType">第一步传 1 第二步传2</param>
|
||
///// <returns></returns>
|
||
//public EnterpriseWechatJsSdkResp GetJsSdk(string url, int tokenType = 1)
|
||
//{
|
||
|
||
// EnterpriseWechatJsSdkResp result = new EnterpriseWechatJsSdkResp();
|
||
|
||
|
||
|
||
// string corpId = companyConfig.Corpid;
|
||
// string agentId = companyConfig.CustomerAgentidId.Replace("\r\n", "");
|
||
// string secret = companyConfig.CustomerDetailSecret;
|
||
// string ticket = "";
|
||
|
||
// string token = GetAccessToken(corpId, secret);
|
||
// long timeStamp = 0;
|
||
// string nonceStr = "";
|
||
|
||
// if (tokenType == 1)
|
||
// {
|
||
// ticket = GetTicket(token);
|
||
// timeStamp = long.Parse(JSSDKHelper.GetTimestamp());
|
||
// nonceStr = JSSDKHelper.GetNoncestr();
|
||
// }
|
||
// else if (tokenType == 2)
|
||
// {
|
||
// result = _myCodeCacheService.Get<EnterpriseWechatJsSdkResp>(companyConfig.Corpid + "1");
|
||
// ticket = GetApplicationTicket(token);
|
||
// timeStamp = long.Parse(result.Timestamp);
|
||
// nonceStr = result.NonceStr;
|
||
// }
|
||
// else
|
||
// {
|
||
// return null;
|
||
// }
|
||
|
||
// //var decodeUrl = HttpUtility.UrlDecode(url);
|
||
|
||
// string signature = GetSignature(ticket, nonceStr, timeStamp, url);
|
||
// result.CorpId = corpId;
|
||
// result.AgentId = agentId;
|
||
// result.NonceStr = nonceStr;
|
||
// result.Signature = signature;
|
||
// result.Timestamp = timeStamp.ToString();
|
||
// //LogHelper.Info("result:");
|
||
// //LogHelper.Info(result);
|
||
// _myCodeCacheService.Set(cacheKey, result, new TimeSpan(1, 0, 0));
|
||
|
||
// return result;
|
||
|
||
//}
|
||
//#endregion
|
||
|
||
#region GetDepartmentList(得到部门列表)
|
||
/// <summary>
|
||
/// 取得通讯录的部门列表,使用通讯录Token
|
||
/// </summary>
|
||
/// <param name="accessToken">Token</param>
|
||
/// <returns></returns>
|
||
public List<DepartmentHasChild> GetDepartmentList(string accessToken, string corpId, string secret)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(accessToken))
|
||
{
|
||
accessToken = GetAccessToken(corpId, secret);
|
||
}
|
||
var url = $"{apiUrl}cgi-bin/department/list?access_token={accessToken}";
|
||
|
||
var resp = Get<DepartmentResp>(url);
|
||
|
||
if (resp.Department == null || resp.Department.Count == 0) { return new List<DepartmentHasChild> { }; }
|
||
|
||
return GetDepartmentChild(resp.Department, 0);
|
||
}
|
||
#endregion
|
||
|
||
#region GetDepartmentMemberList(得到部门的员工列表)
|
||
/// <summary>
|
||
/// 得到部门的员工列表
|
||
/// </summary>
|
||
/// <param name="accessToken">通讯录的token</param>
|
||
/// <param name="departmentId">部门id</param>
|
||
/// <param name="fetch_child">是否递归获取子部门下面的成员:1-递归获取,0-只获取本部门</param>
|
||
/// <returns></returns>
|
||
public List<DepartmentMemberInfo> GetDepartmentMemberList(string accessToken, long departmentId, string corpId, string secret, int fetch_child = 0)
|
||
{
|
||
//if (departmentId == 0) { throw new BaseException("部门id不能为0"); }
|
||
if (string.IsNullOrWhiteSpace(accessToken))
|
||
{
|
||
accessToken = GetAccessToken(corpId, secret);
|
||
}
|
||
var url = $"{apiUrl}cgi-bin/user/simplelist?access_token={accessToken}&department_id={departmentId}&fetch_child={fetch_child}";//是否递归获取子部门下面的成员:1-递归获取,0-只获取本部门
|
||
|
||
var resp = Get<DepartmentMemberInfoResp>(url);
|
||
|
||
if (resp.Userlist == null || resp.Userlist.Count == 0) { return new List<DepartmentMemberInfo> { }; }
|
||
|
||
return resp.Userlist;
|
||
}
|
||
#endregion
|
||
|
||
#region GetEmployeeInfo(获取员工信息)
|
||
/// <summary>
|
||
/// 获取员工信息
|
||
/// </summary>
|
||
/// <param name="userId">员工id</param>
|
||
/// <returns></returns>
|
||
public EnterpriseEmployeeInfo GetEmployeeInfo(string userId, string corpId, string secret)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(userId)) { throw new BaseException("员工id不能为空"); }
|
||
var accessToken = GetAccessToken(corpId, secret);
|
||
var url = $"{apiUrl}/cgi-bin/user/get?access_token={accessToken}&userid={userId}";
|
||
var webUtils = new WebUtils();
|
||
var resp = webUtils.DoGet<EnterpriseEmployeeInfo>(url, null);
|
||
return resp;
|
||
}
|
||
#endregion
|
||
//#region GetLoginUserInfo(获取登录用户信息)
|
||
///// <summary>
|
||
///// 获取登录用户信息
|
||
///// </summary>
|
||
///// <param name="code"></param>
|
||
///// <param name="corpId"></param>
|
||
///// <param name="secret">自建应用secret</param>
|
||
///// <returns></returns>
|
||
//public UserInfoResp GetLoginUserInfo(string code, string corpId = "", string secret = "")
|
||
//{
|
||
// var accessToken = GetAccessToken(corpId, secret);
|
||
// var url = $"{apiUrl}/cgi-bin/user/getuserinfo?access_token={accessToken}&code={code}";
|
||
// var result = Get<UserInfoResp>(url);
|
||
// return result;
|
||
//}
|
||
//#endregion
|
||
|
||
#region 从url读取内容到内存Stream流中
|
||
/// <summary>
|
||
/// 从url读取内容到内存Stream流中
|
||
/// </summary>
|
||
/// <param name="url"></param>
|
||
/// <returns></returns>
|
||
public Stream DownLoadFielToStream(string url)
|
||
{
|
||
var wreq = HttpWebRequest.Create(url) as HttpWebRequest;
|
||
HttpWebResponse response = wreq.GetResponse() as HttpWebResponse;
|
||
MemoryStream ms = null;
|
||
using (var stream = response.GetResponseStream())
|
||
{
|
||
Byte[] buffer = new Byte[response.ContentLength];
|
||
int offset = 0, actuallyRead = 0;
|
||
do
|
||
{
|
||
actuallyRead = stream.Read(buffer, offset, buffer.Length - offset);
|
||
offset += actuallyRead;
|
||
}
|
||
while (actuallyRead > 0);
|
||
ms = new MemoryStream(buffer);
|
||
}
|
||
response.Close();
|
||
return ms;
|
||
}
|
||
#endregion
|
||
|
||
|
||
|
||
|
||
//#region openid转userid
|
||
///// <summary>
|
||
///// openid转userid
|
||
///// </summary>
|
||
///// <param name="openid"></param>
|
||
///// <param name="corpId"></param>
|
||
///// <param name="secret">通讯录secret</param>
|
||
///// <returns></returns>
|
||
//public OpenIdChangeUseridResp GetUseridByOpenid(string openid, string corpId = "", string secret = "")
|
||
//{
|
||
// var accessToken = GetAccessToken(corpId, secret);
|
||
// var url = $"{apiUrl}cgi-bin/user/convert_to_userid?access_token={accessToken}";
|
||
// var webUtils = new WebUtils();
|
||
// var resp = webUtils.DoGet<OpenIdChangeUseridResp>(url, null);
|
||
// return resp;
|
||
//}
|
||
//#endregion
|
||
|
||
|
||
#region userid转openid
|
||
/// <summary>
|
||
/// userid转openid
|
||
/// </summary>
|
||
/// <param name="openid"></param>
|
||
/// <param name="corpId"></param>
|
||
/// <param name="secret">通讯录secret</param>
|
||
/// <returns></returns>
|
||
public UserIdChangeOpenIdResp GetOpenidByUserid(string userId, string corpId = "", string secret = "")
|
||
{
|
||
var accessToken = GetAccessToken(corpId, secret);
|
||
var url = $"{apiUrl}cgi-bin/user/convert_to_openid?access_token={accessToken}";
|
||
string json = $@"{{
|
||
""userid"": ""{userId}""
|
||
}}";
|
||
var webUtils = new WebUtils();
|
||
var resp = webUtils.DoPostJson(url, json);
|
||
UserIdChangeOpenIdResp resut = JsonHelper.ToObject<UserIdChangeOpenIdResp>(resp);
|
||
return resut;
|
||
}
|
||
#endregion
|
||
|
||
}
|
||
}
|