添加项目文件。
This commit is contained in:
455
common/Tnb.Thirdparty/DingDing/DingUtil.cs
vendored
Normal file
455
common/Tnb.Thirdparty/DingDing/DingUtil.cs
vendored
Normal file
@@ -0,0 +1,455 @@
|
||||
using DingTalk.Api;
|
||||
using DingTalk.Api.Request;
|
||||
using DingTalk.Api.Response;
|
||||
using Mapster;
|
||||
using static DingTalk.Api.Request.OapiRobotSendRequest;
|
||||
using static DingTalk.Api.Response.OapiV2DepartmentListsubResponse;
|
||||
using static DingTalk.Api.Response.OapiV2UserListResponse;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.DingDing;
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉.
|
||||
/// </summary>
|
||||
public class DingUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 访问令牌.
|
||||
/// </summary>
|
||||
public string token { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数.
|
||||
/// </summary>
|
||||
/// <param name="appKey">企业号ID.</param>
|
||||
/// <param name="appSecret">凭证密钥.</param>
|
||||
public DingUtil(string appKey, string appSecret)
|
||||
{
|
||||
token = GetDingToken(appKey, appSecret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数.
|
||||
/// </summary>
|
||||
/// <param name="appKey">企业号ID.</param>
|
||||
/// <param name="appSecret">凭证密钥.</param>
|
||||
public DingUtil()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉token.
|
||||
/// </summary>
|
||||
/// <param name="appKey">企业号ID.</param>
|
||||
/// <param name="appSecret">凭证密钥.</param>
|
||||
/// <returns></returns>
|
||||
public string GetDingToken(string appKey, string appSecret)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokenurl = "https://oapi.dingtalk.com/gettoken";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(tokenurl);
|
||||
OapiGettokenRequest req = new OapiGettokenRequest();
|
||||
req.SetHttpMethod("GET");
|
||||
req.Appkey = appKey;
|
||||
req.Appsecret = appSecret;
|
||||
OapiGettokenResponse response = client.Execute(req);
|
||||
if (response.Errcode == 0)
|
||||
{
|
||||
// 过期时间
|
||||
var timeout = DateTime.Now.Subtract(DateTime.Now.AddSeconds(response.ExpiresIn));
|
||||
return response.AccessToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("获取钉钉Token失败,失败原因:" + response.Errmsg);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉登录.
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
public object DingLogin(string code)
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/user/getuserinfo";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiUserGetuserinfoRequest req = new OapiUserGetuserinfoRequest();
|
||||
req.Code = code;
|
||||
req.SetHttpMethod("GET");
|
||||
OapiUserGetuserinfoResponse rsp = client.Execute(req, token);
|
||||
if (rsp.IsError)
|
||||
throw new Exception(rsp.ErrMsg);
|
||||
return rsp.Body;
|
||||
}
|
||||
|
||||
#region 用户
|
||||
|
||||
/// <summary>
|
||||
/// 添加钉钉用户.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
/// <returns></returns>
|
||||
public string CreateUser(DingUserParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = GetUserIdByMobile(dingModel.Mobile);
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
return userId;
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/user/create";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2UserCreateRequest req = dingModel.Adapt<OapiV2UserCreateRequest>();
|
||||
OapiV2UserCreateResponse rsp = client.Execute(req, token);
|
||||
if (!rsp.IsError && rsp.Result != null)
|
||||
{
|
||||
return rsp.Result.Userid;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = rsp.Errmsg;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改钉钉用户.
|
||||
/// </summary>
|
||||
/// <param name="dingModel">钉钉用户参数.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public bool UpdateUser(DingUserParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/user/update";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2UserUpdateRequest req = dingModel.Adapt<OapiV2UserUpdateRequest>();
|
||||
OapiV2UserUpdateResponse rsp = client.Execute(req, token);
|
||||
if (rsp.IsError)
|
||||
msg = rsp.Errmsg;
|
||||
return rsp.Errcode == 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改钉钉用户.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
public void DeleteUser(DingUserParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/user/delete";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2UserDeleteRequest req = dingModel.Adapt<OapiV2UserDeleteRequest>();
|
||||
OapiV2UserDeleteResponse rsp = client.Execute(req, token);
|
||||
if (rsp.IsError)
|
||||
{
|
||||
msg = rsp.Errmsg;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据手机号查询用户
|
||||
/// 员工离职后,无法再通过手机号获取userId.
|
||||
/// </summary>
|
||||
/// <param name="mobile"></param>
|
||||
/// <returns></returns>
|
||||
public string GetUserIdByMobile(string mobile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest() { Mobile = mobile };
|
||||
OapiV2UserGetbymobileResponse rsp = client.Execute(req, token);
|
||||
if (!rsp.IsError && rsp.Result != null)
|
||||
{
|
||||
return rsp.Result.Userid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有用户详情.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<ListUserResponseDomain> GetUserList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/user/list";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
var userList = new List<ListUserResponseDomain>();
|
||||
var depList = new List<long>();
|
||||
depList.Add(1);
|
||||
GetDepIdList(1, ref depList);
|
||||
if (depList.Count > 0)
|
||||
{
|
||||
foreach (var item in depList)
|
||||
{
|
||||
OapiV2UserListRequest req = new OapiV2UserListRequest() { DeptId = item, Cursor = 0, Size = 10 };
|
||||
OapiV2UserListResponse rsp = client.Execute(req, token);
|
||||
if (rsp.Errcode == 0 && rsp.Errmsg == "ok" && rsp.Result != null && rsp.Result.List != null)
|
||||
{
|
||||
userList = userList.Union(rsp.Result.List).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userList;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 部门
|
||||
|
||||
/// <summary>
|
||||
/// 添加钉钉部门.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <returns></returns>
|
||||
public string CreateDep(DingDepartmentParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/department/create";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2DepartmentCreateRequest req = dingModel.Adapt<OapiV2DepartmentCreateRequest>();
|
||||
OapiV2DepartmentCreateResponse rsp = client.Execute(req, token);
|
||||
if (rsp.Errcode == 0)
|
||||
{
|
||||
if (!rsp.IsError && rsp.Result != null)
|
||||
{
|
||||
return rsp.Result.DeptId.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = string.Format("【组织{0}创建失败】{1}", dingModel.Name, rsp.Errmsg);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = string.Format("【组织{0}创建失败】{1}", dingModel.Name, rsp.Errmsg);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = string.Format("【组织{0}创建失败】{1}", dingModel.Name, ex.Message);
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改钉钉部门.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
public bool UpdateDep(DingDepartmentParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/department/update";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2DepartmentUpdateRequest req = dingModel.Adapt<OapiV2DepartmentUpdateRequest>();
|
||||
OapiV2DepartmentUpdateResponse rsp = client.Execute(req, token);
|
||||
if (!rsp.IsError)
|
||||
{
|
||||
msg = rsp.IsError ? string.Format("【组织{0}修改失败】{1}", dingModel.Name, rsp.Errmsg) : string.Empty;
|
||||
return !rsp.IsError;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = string.Format("【组织{0}修改失败】{1}", dingModel.Name, rsp.Errmsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = string.Format("【组织{0}修改失败】{1}", dingModel.Name, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除钉钉部门.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
public bool DeleteDep(DingDepartmentParameter dingModel, ref string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/department/delete";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2DepartmentDeleteRequest req = dingModel.Adapt<OapiV2DepartmentDeleteRequest>();
|
||||
OapiV2DepartmentDeleteResponse rsp = client.Execute(req, token);
|
||||
if (!rsp.IsError)
|
||||
{
|
||||
msg = rsp.IsError ? rsp.Errmsg : string.Empty;
|
||||
return !rsp.IsError;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = rsp.Errmsg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取部门列表.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<DeptBaseResponseDomain> GetDepList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var depId = new List<long>();
|
||||
depId.Add(1);
|
||||
GetDepIdList(1, ref depId);
|
||||
var list = new List<DeptBaseResponseDomain>();
|
||||
if (depId.Count > 0)
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
|
||||
foreach (var item in depId)
|
||||
{
|
||||
req.DeptId = item;
|
||||
OapiV2DepartmentListsubResponse rsp = client.Execute(req, token);
|
||||
if (rsp.Errcode == 0 && rsp.Errmsg == "ok" && rsp.Result != null)
|
||||
{
|
||||
list = list.Union(rsp.Result).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取部门id列表.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void GetDepIdList(long depId, ref List<long> ids)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/v2/department/listsubid";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiV2DepartmentListsubidRequest req = new OapiV2DepartmentListsubidRequest();
|
||||
req.DeptId = depId;
|
||||
OapiV2DepartmentListsubidResponse rsp = client.Execute(req, token);
|
||||
if (rsp.Errcode == 0 && rsp.Errmsg == "ok" && rsp.Result != null)
|
||||
{
|
||||
ids = ids.Union(rsp.Result.DeptIdList).ToList();
|
||||
foreach (var item in rsp.Result.DeptIdList)
|
||||
{
|
||||
GetDepIdList(item, ref ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 发送工作消息.
|
||||
/// </summary>
|
||||
/// <param name="dingModel"></param>
|
||||
public void SendWorkMsg(DingWorkMessageParameter dingModel)
|
||||
{
|
||||
var url = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request()
|
||||
{
|
||||
AgentId = long.Parse(dingModel.agentId),
|
||||
UseridList = dingModel.toUsers,
|
||||
Msg = dingModel.msg
|
||||
};
|
||||
request.SetHttpMethod("POST");
|
||||
OapiMessageCorpconversationAsyncsendV2Response response = client.Execute(request, token);
|
||||
if (response.IsError)
|
||||
{
|
||||
throw new Exception(response.ErrMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送群聊消息.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="content"></param>
|
||||
public void SendGroupMsg(string url, string content)
|
||||
{
|
||||
DefaultDingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.Msgtype = "text";
|
||||
TextDomain text = new TextDomain();
|
||||
text.Content = content;
|
||||
request.Text_ = text;
|
||||
request.SetHttpMethod("POST");
|
||||
OapiRobotSendResponse response = client.Execute(request);
|
||||
if (response.IsError)
|
||||
{
|
||||
throw new Exception(response.ErrMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
112
common/Tnb.Thirdparty/DingDing/Internal/DingDepartmentParameter.cs
vendored
Normal file
112
common/Tnb.Thirdparty/DingDing/Internal/DingDepartmentParameter.cs
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
namespace JNPF.Extras.Thirdparty.DingDing;
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉部门参数.
|
||||
/// </summary>
|
||||
public class DingDepartmentParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 部门群是否包含外包部门.
|
||||
/// </summary>
|
||||
public bool? GroupContainOuterDept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父部门ID.
|
||||
/// </summary>
|
||||
public long? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门员工可见员工列表.
|
||||
/// </summary>
|
||||
public string OuterPermitUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门员工可见部门列表.
|
||||
/// </summary>
|
||||
public string OuterPermitDepts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本部门成员是否只能看到所在部门及下级部门通讯录.
|
||||
/// </summary>
|
||||
public bool? OuterDeptOnlySelf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否限制本部门成员查看通讯录.
|
||||
/// </summary>
|
||||
public bool? OuterDept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 企业群群主的userid.
|
||||
/// </summary>
|
||||
public string OrgDeptOwner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 在父部门中的排序值.
|
||||
/// </summary>
|
||||
public long? Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称,长度限制为1~64个字符,不允许包含字符‘-’‘,’以及‘,’.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 语言.
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否隐藏本部门.
|
||||
/// </summary>
|
||||
public bool? HideDept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门群是否包含子部门.
|
||||
/// </summary>
|
||||
public bool? GroupContainSubDept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指定可以查看本部门的用户userid列表.
|
||||
/// </summary>
|
||||
public string UserPermits { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门群是否包含隐藏部门.
|
||||
/// </summary>
|
||||
public bool? GroupContainHiddenDept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展.
|
||||
/// </summary>
|
||||
public string Extension { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指定可以查看本部门的其他部门列表.
|
||||
/// </summary>
|
||||
public string DeptPermits { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门的主管userid列表.
|
||||
/// </summary>
|
||||
public string DeptManagerUseridList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门ID.
|
||||
/// </summary>
|
||||
public long? DeptId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否创建一个关联此部门的企业群.
|
||||
/// </summary>
|
||||
public bool? CreateDeptGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当部门群已经创建后,有新人加入部门时是否会自动加入该群.
|
||||
/// </summary>
|
||||
public bool? AutoAddUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门标识字段,开发者可用该字段来唯一标识一个部门,并与钉钉外部通讯录里的部门做映射.
|
||||
/// </summary>
|
||||
public string SourceIdentifier { get; set; }
|
||||
}
|
||||
102
common/Tnb.Thirdparty/DingDing/Internal/DingUserParameter.cs
vendored
Normal file
102
common/Tnb.Thirdparty/DingDing/Internal/DingUserParameter.cs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
namespace JNPF.Extras.Thirdparty.DingDing;
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉用户.
|
||||
/// </summary>
|
||||
public class DingUserParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 入职时间,Unix时间戳,单位毫秒.
|
||||
/// </summary>
|
||||
public long? HiredDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注,长度最大2000个字符.
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工的企业邮箱.
|
||||
/// </summary>
|
||||
public string OrgEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工名称.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 手机号码,企业内必须唯一,不可重复.
|
||||
/// </summary>
|
||||
public string Mobile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 语言.
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工工号.
|
||||
/// </summary>
|
||||
public string JobNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉专属帐号初始密码.
|
||||
/// </summary>
|
||||
public string InitPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职位.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否号码隐藏.
|
||||
/// </summary>
|
||||
public bool? HideMobile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展属性,可以设置多种属性,最大长度2000个字符.
|
||||
/// </summary>
|
||||
public string Extension { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工邮箱,长度最大50个字符。企业内必须唯一,不可重复.
|
||||
/// </summary>
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工在对应的部门中的职位.
|
||||
/// </summary>
|
||||
public string DeptTitleList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工在对应的部门中的排序.
|
||||
/// </summary>
|
||||
public string DeptOrderList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门id列表.
|
||||
/// </summary>
|
||||
public string DeptIdList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 员工唯一标识ID(不可修改),企业内必须唯一.
|
||||
/// </summary>
|
||||
public string Userid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 办公地点.
|
||||
/// </summary>
|
||||
public string WorkPlace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启高管模式.
|
||||
/// </summary>
|
||||
public bool? SeniorMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分机号.
|
||||
/// </summary>
|
||||
public string Telephone { get; set; }
|
||||
}
|
||||
22
common/Tnb.Thirdparty/DingDing/Internal/DingWorkMessageParameter.cs
vendored
Normal file
22
common/Tnb.Thirdparty/DingDing/Internal/DingWorkMessageParameter.cs
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace JNPF.Extras.Thirdparty.DingDing;
|
||||
|
||||
/// <summary>
|
||||
/// 钉钉消息.
|
||||
/// </summary>
|
||||
public class DingWorkMessageParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 接收人.
|
||||
/// </summary>
|
||||
public string toUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息内容.
|
||||
/// </summary>
|
||||
public string msg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微应用的AgentID.
|
||||
/// </summary>
|
||||
public string agentId { get; set; }
|
||||
}
|
||||
37
common/Tnb.Thirdparty/Email/Internal/MailFileParameterInfo.cs
vendored
Normal file
37
common/Tnb.Thirdparty/Email/Internal/MailFileParameterInfo.cs
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace JNPF.Extras.Thirdparty.Email;
|
||||
|
||||
/// <summary>
|
||||
/// 邮件文件参数信息.
|
||||
/// </summary>
|
||||
public class MailFileParameterInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件id.
|
||||
/// </summary>
|
||||
public string fileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件名.
|
||||
/// </summary>
|
||||
public string fileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小.
|
||||
/// </summary>
|
||||
public string fileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件时间.
|
||||
/// </summary>
|
||||
public DateTime fileTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件状态.
|
||||
/// </summary>
|
||||
public string fileState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件名.
|
||||
/// </summary>
|
||||
public string name { get; set; }
|
||||
}
|
||||
62
common/Tnb.Thirdparty/Email/Internal/MailInfo.cs
vendored
Normal file
62
common/Tnb.Thirdparty/Email/Internal/MailInfo.cs
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
namespace JNPF.Extras.Thirdparty.Email;
|
||||
|
||||
/// <summary>
|
||||
/// 邮件信息.
|
||||
/// </summary>
|
||||
public class MailInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息标识符.
|
||||
/// </summary>
|
||||
public string UID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件人邮箱.
|
||||
/// </summary>
|
||||
public string To { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件人名称
|
||||
/// </summary>
|
||||
public string ToName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 抄送人.
|
||||
/// </summary>
|
||||
public string CC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 抄送人名称.
|
||||
/// </summary>
|
||||
public string CCName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密送人.
|
||||
/// </summary>
|
||||
public string Bcc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密送人名称.
|
||||
/// </summary>
|
||||
public string BccName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息主题.
|
||||
/// </summary>
|
||||
public string Subject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文本格式.
|
||||
/// </summary>
|
||||
public string BodyText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 附件.
|
||||
/// </summary>
|
||||
public List<MailFileParameterInfo> Attachment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件时间.
|
||||
/// </summary>
|
||||
public DateTime Date { get; set; }
|
||||
}
|
||||
47
common/Tnb.Thirdparty/Email/Internal/MailParameterInfo.cs
vendored
Normal file
47
common/Tnb.Thirdparty/Email/Internal/MailParameterInfo.cs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace JNPF.Extras.Thirdparty.Email;
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱参数信息.
|
||||
/// </summary>
|
||||
public class MailParameterInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// POP3服务.
|
||||
/// </summary>
|
||||
public string POP3Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// POP3端口.
|
||||
/// </summary>
|
||||
public int POP3Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SMTP服务.
|
||||
/// </summary>
|
||||
public string SMTPHost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SMTP端口.
|
||||
/// </summary>
|
||||
public int SMTPPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账户.
|
||||
/// </summary>
|
||||
public string Account { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账户名称.
|
||||
/// </summary>
|
||||
public string AccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码.
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SSL.
|
||||
/// </summary>
|
||||
public bool Ssl { get; set; } = false;
|
||||
}
|
||||
276
common/Tnb.Thirdparty/Email/MailUtil.cs
vendored
Normal file
276
common/Tnb.Thirdparty/Email/MailUtil.cs
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
using System.Text;
|
||||
using JNPF.Common.Configuration;
|
||||
using JNPF.Common.Extension;
|
||||
using JNPF.Common.Security;
|
||||
using MailKit.Net.Pop3;
|
||||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using MimeKit.Text;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.Email;
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱帮助类.
|
||||
/// </summary>
|
||||
public class MailUtil
|
||||
{
|
||||
private static string mailFilePath = FileVariable.EmailFilePath;
|
||||
|
||||
/// <summary>
|
||||
/// 发送:协议Smtp.
|
||||
/// </summary>
|
||||
/// <param name="mailConfig">配置</param>
|
||||
/// <param name="mailModel">信息</param>
|
||||
public static void Send(MailParameterInfo mailConfig, MailInfo mailModel)
|
||||
{
|
||||
MimeMessage message = new MimeMessage();
|
||||
|
||||
// 发送方信息
|
||||
message.From.AddRange(new MailboxAddress[] { new MailboxAddress(mailConfig.AccountName, mailConfig.Account) });
|
||||
|
||||
// 发件人
|
||||
if (!string.IsNullOrEmpty(mailModel.To))
|
||||
{
|
||||
List<MailboxAddress> toAddress = new List<MailboxAddress>();
|
||||
foreach (var item in mailModel.To.Split(','))
|
||||
{
|
||||
toAddress.Add(new MailboxAddress(item));
|
||||
}
|
||||
|
||||
message.To.AddRange(toAddress.ToArray());
|
||||
}
|
||||
|
||||
// 抄送人
|
||||
if (!string.IsNullOrEmpty(mailModel.CC))
|
||||
{
|
||||
List<MailboxAddress> ccAddress = new List<MailboxAddress>();
|
||||
foreach (var item in mailModel.CC.Split(','))
|
||||
{
|
||||
ccAddress.Add(new MailboxAddress(item));
|
||||
}
|
||||
|
||||
message.Cc.AddRange(ccAddress.ToArray());
|
||||
}
|
||||
|
||||
// 密送人
|
||||
if (!string.IsNullOrEmpty(mailModel.Bcc))
|
||||
{
|
||||
List<MailboxAddress> bccAddress = new List<MailboxAddress>();
|
||||
foreach (var item in mailModel.Bcc.Split(','))
|
||||
{
|
||||
bccAddress.Add(new MailboxAddress(item));
|
||||
}
|
||||
|
||||
message.Bcc.AddRange(bccAddress.ToArray());
|
||||
}
|
||||
|
||||
message.Subject = mailModel.Subject;
|
||||
TextPart body = new TextPart(TextFormat.Html) { Text = mailModel.BodyText };
|
||||
MimeEntity entity = body;
|
||||
|
||||
// 附件
|
||||
if (mailModel.Attachment != null)
|
||||
{
|
||||
var mult = new Multipart("mixed") { body };
|
||||
foreach (var attachment in mailModel.Attachment)
|
||||
{
|
||||
var file = new FileInfo(Path.Combine(mailFilePath , attachment.fileId));
|
||||
if (file.Exists)
|
||||
{
|
||||
var mimePart = new MimePart();
|
||||
mimePart.Content = new MimeContent(file.OpenRead());
|
||||
mimePart.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment);
|
||||
mimePart.ContentTransferEncoding = ContentEncoding.Base64;
|
||||
mimePart.FileName = attachment.fileName;
|
||||
mult.Add(mimePart);
|
||||
}
|
||||
}
|
||||
|
||||
entity = mult;
|
||||
}
|
||||
|
||||
message.Body = entity;
|
||||
message.Date = DateTime.Now;
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
client.Connect(mailConfig.SMTPHost, mailConfig.SMTPPort, mailConfig.Ssl);
|
||||
client.Authenticate(mailConfig.Account, mailConfig.Password);
|
||||
client.Send(message);
|
||||
client.Disconnect(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取:协议Pop3.
|
||||
/// </summary>
|
||||
/// <param name="mailConfig">配置.</param>
|
||||
/// <param name="receiveCount">已收邮件数、注意:如果已收邮件数和邮件数量一致则不获取.</param>
|
||||
/// <returns></returns>
|
||||
public static List<MailInfo> Get(MailParameterInfo mailConfig, int receiveCount)
|
||||
{
|
||||
var resultList = new List<MailInfo>();
|
||||
using (var client = new Pop3Client())
|
||||
{
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
client.Connect(mailConfig.POP3Host, mailConfig.POP3Port, mailConfig.Ssl);
|
||||
client.Authenticate(mailConfig.Account, mailConfig.Password);
|
||||
if (receiveCount == client.Count)
|
||||
return resultList;
|
||||
for (int i = client.Count - 1; receiveCount <= i; i--)
|
||||
{
|
||||
var message = client.GetMessage(i);
|
||||
var from = (MailboxAddress)message.From[0];
|
||||
var attachment = message.Attachments;
|
||||
resultList.Add(new MailInfo()
|
||||
{
|
||||
UID = message.MessageId,
|
||||
To = from.Address,
|
||||
ToName = from.Name,
|
||||
Subject = message.Subject,
|
||||
BodyText = message.HtmlBody,
|
||||
Attachment = GetEmailAttachments(attachment, message.MessageId),
|
||||
Date = message.Date.ToString().ParseToDateTime(),
|
||||
});
|
||||
}
|
||||
|
||||
client.Disconnect(true);
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除:协议Pop3.
|
||||
/// </summary>
|
||||
/// <param name="mailConfig">配置.</param>
|
||||
/// <param name="messageId">messageId.</param>
|
||||
public static void Delete(MailParameterInfo mailConfig, string messageId)
|
||||
{
|
||||
using (var client = new Pop3Client())
|
||||
{
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
client.Connect(mailConfig.POP3Host, mailConfig.POP3Port, mailConfig.Ssl);
|
||||
client.Authenticate(mailConfig.Account, mailConfig.Password);
|
||||
for (int i = 0; i < client.Count; i++)
|
||||
{
|
||||
if (client.GetMessage(i).MessageId == messageId)
|
||||
{
|
||||
client.DeleteMessage(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证连接:协议Smtp、Pop3.
|
||||
/// </summary>
|
||||
/// <param name="mailConfig">配置.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckConnected(MailParameterInfo mailConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(mailConfig.SMTPHost))
|
||||
{
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
client.Connect(mailConfig.SMTPHost, mailConfig.SMTPPort, mailConfig.Ssl);
|
||||
client.Authenticate(mailConfig.Account, mailConfig.Password);
|
||||
client.Disconnect(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(mailConfig.POP3Host))
|
||||
{
|
||||
using (var client = new Pop3Client())
|
||||
{
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
client.Connect(mailConfig.POP3Host, mailConfig.POP3Port, mailConfig.Ssl);
|
||||
client.Authenticate(mailConfig.Account, mailConfig.Password);
|
||||
client.Disconnect(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Write("邮箱验证失败原因:" + ex + ",失败详情:" + ex.StackTrace);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Method
|
||||
|
||||
/// <summary>
|
||||
/// 获取邮件附件.
|
||||
/// </summary>
|
||||
/// <param name="attachments"></param>
|
||||
/// <param name="messageId"></param>
|
||||
/// <returns></returns>
|
||||
private static List<MailFileParameterInfo> GetEmailAttachments(IEnumerable<MimeEntity> attachments, string messageId)
|
||||
{
|
||||
var resultList = new List<MailFileParameterInfo>();
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
if (attachment.IsAttachment)
|
||||
{
|
||||
var fileName = attachment.ContentDisposition?.FileName ?? attachment.ContentType.Name;
|
||||
var fileId = YitIdHelper.NextId().ToString() + "_" + fileName;
|
||||
var filePath = Path.Combine(mailFilePath, fileId);
|
||||
using (var stream = File.Create(filePath))
|
||||
{
|
||||
if (attachment is MessagePart rfc822)
|
||||
{
|
||||
rfc822.Message.WriteTo(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
var part = (MimePart)attachment;
|
||||
part.Content.DecodeTo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
var mailFileInfo = new FileInfo(filePath);
|
||||
resultList.Add(new MailFileParameterInfo { fileId = fileId, fileName = fileName, fileSize = FileHelper.ToFileSize(mailFileInfo.Length) });
|
||||
}
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为Base64.
|
||||
/// </summary>
|
||||
/// <param name="inputStr"></param>
|
||||
/// <param name="encoding"></param>
|
||||
/// <returns></returns>
|
||||
private static string ConvertToBase64(string inputStr, Encoding encoding)
|
||||
{
|
||||
return Convert.ToBase64String(encoding.GetBytes(inputStr));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换报头为Base64.
|
||||
/// </summary>
|
||||
/// <param name="inputStr"></param>
|
||||
/// <param name="encoding"></param>
|
||||
/// <returns></returns>
|
||||
private static string ConvertHeaderToBase64(string inputStr, Encoding encoding)
|
||||
{
|
||||
var encode = !string.IsNullOrEmpty(inputStr) && inputStr.Any(c => c > 127);
|
||||
if (encode)
|
||||
{
|
||||
return "=?" + encoding.WebName + "?B?" + ConvertToBase64(inputStr, encoding) + "?=";
|
||||
}
|
||||
|
||||
return inputStr;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
84
common/Tnb.Thirdparty/JSEngine/JsEngineUtil.cs
vendored
Normal file
84
common/Tnb.Thirdparty/JSEngine/JsEngineUtil.cs
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
using JavaScriptEngineSwitcher.V8;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.JSEngine;
|
||||
|
||||
/// <summary>
|
||||
/// js处理引擎.
|
||||
/// </summary>
|
||||
public class JsEngineUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行Js(返回结果请用result).
|
||||
/// 如:var result = function(a,b){}.
|
||||
/// </summary>
|
||||
/// <param name="jsContent">js内容.</param>
|
||||
/// <param name="args">参数.</param>
|
||||
/// <returns></returns>
|
||||
public static object CallFunction(string jsContent, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
V8JsEngine engine = new V8JsEngine();
|
||||
engine.Execute(jsContent);
|
||||
return engine.CallFunction("result", args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("不支持的JS数据");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行聚合函数Js(返回结果请用result).
|
||||
/// 如:var result = function(a,b){}.
|
||||
/// </summary>
|
||||
/// <param name="jsContent">js内容.</param>
|
||||
/// <param name="args">参数.</param>
|
||||
/// <returns></returns>
|
||||
public static object AggreFunction(string jsContent, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var aggreFunc = "function getNum(val) {\n" +
|
||||
" return isNaN(val) ? 0 : Number(val)\n" +
|
||||
"};\n" +
|
||||
"// 求和\n" +
|
||||
"function SUM() {\n" +
|
||||
" var value = 0\n" +
|
||||
" for (var i = 0; i < arguments.length; i++) {\n" +
|
||||
" value += getNum(arguments[i])\n" +
|
||||
" }\n" +
|
||||
" return value\n" +
|
||||
"};\n" +
|
||||
"// 求差\n" +
|
||||
"function SUBTRACT(num1, num2) {\n" +
|
||||
" return getNum(num1) - getNum(num2)\n" +
|
||||
"};\n" +
|
||||
"// 相乘\n" +
|
||||
"function PRODUCT() {\n" +
|
||||
" var value = 1\n" +
|
||||
" for (var i = 0; i < arguments.length; i++) {\n" +
|
||||
" value = value * getNum(arguments[i])\n" +
|
||||
" }\n" +
|
||||
" return value\n" +
|
||||
"};\n" +
|
||||
"// 相除\n" +
|
||||
"function DIVIDE(num1, num2) {\n" +
|
||||
" return getNum(num1) / (getNum(num2) === 0 ? 1 : getNum(num2))\n" +
|
||||
"};\n" +
|
||||
"// 获取参数的数量\n" +
|
||||
"function COUNT() {\n" +
|
||||
" return arguments.length\n" +
|
||||
"};\n";
|
||||
aggreFunc = aggreFunc + "var result =function(){ return " + jsContent + "}";
|
||||
|
||||
V8JsEngine engine = new V8JsEngine();
|
||||
engine.Execute(aggreFunc);
|
||||
return engine.CallFunction("result", args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
22
common/Tnb.Thirdparty/Sms/Internal/SmsMessage.cs
vendored
Normal file
22
common/Tnb.Thirdparty/Sms/Internal/SmsMessage.cs
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace JNPF.Extras.Thirdparty.Sms;
|
||||
|
||||
/// <summary>
|
||||
/// 短信消息.
|
||||
/// </summary>
|
||||
public class SmsMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求标识.
|
||||
/// </summary>
|
||||
public string RequestId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 验证码.
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
}
|
||||
68
common/Tnb.Thirdparty/Sms/Internal/SmsParameterInfo.cs
vendored
Normal file
68
common/Tnb.Thirdparty/Sms/Internal/SmsParameterInfo.cs
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
namespace JNPF.Extras.Thirdparty.Sms;
|
||||
|
||||
/// <summary>
|
||||
/// 短信参数信息.
|
||||
/// </summary>
|
||||
public class SmsParameterInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 第三方id.
|
||||
/// </summary>
|
||||
public string keyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方秘钥.
|
||||
/// </summary>
|
||||
public string keySecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 签名.
|
||||
/// </summary>
|
||||
public string signName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 访问域名.
|
||||
/// </summary>
|
||||
public string domain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 短信版本.
|
||||
/// </summary>
|
||||
public string version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 手机号(阿里云).
|
||||
/// </summary>
|
||||
public string mobileAli { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 手机号(腾讯云,国内+86).
|
||||
/// </summary>
|
||||
public string[] mobileTx { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板id.
|
||||
/// </summary>
|
||||
public string templateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板参数(阿里云).
|
||||
/// </summary>
|
||||
public string templateParamAli { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板参数(腾讯云).
|
||||
/// </summary>
|
||||
public string[] templateParamTx { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 地域(腾讯云)
|
||||
/// 华北地区:ap-beijing,华南地区:ap-guangzhou,华东地区:ap-nanjing.
|
||||
/// </summary>
|
||||
public string region { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用id(腾讯云).
|
||||
/// </summary>
|
||||
public string appId { get; set; }
|
||||
}
|
||||
187
common/Tnb.Thirdparty/Sms/SmsUtil.cs
vendored
Normal file
187
common/Tnb.Thirdparty/Sms/SmsUtil.cs
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using AlibabaCloud.OpenApiClient.Models;
|
||||
using JNPF.Common.Security;
|
||||
using TencentCloud.Common;
|
||||
using TencentCloud.Common.Profile;
|
||||
using TencentCloud.Sms.V20210111;
|
||||
using TencentCloud.Sms.V20210111.Models;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.Sms;
|
||||
|
||||
public class SmsUtil
|
||||
{
|
||||
#region 阿里云
|
||||
|
||||
/// <summary>
|
||||
/// 发送(阿里云短信).
|
||||
/// </summary>
|
||||
/// <param name="smsModel"></param>
|
||||
/// <returns></returns>
|
||||
public static string SendSmsByAli(SmsParameterInfo smsModel)
|
||||
{
|
||||
var config = new Config
|
||||
{
|
||||
// 您的AccessKey ID
|
||||
AccessKeyId = smsModel.keyId,
|
||||
|
||||
// 您的AccessKey Secret
|
||||
AccessKeySecret = smsModel.keySecret,
|
||||
};
|
||||
config.Endpoint = smsModel.domain;
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Client client = new AlibabaCloud.SDK.Dysmsapi20170525.Client(config);
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest sendSmsRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest();
|
||||
sendSmsRequest.PhoneNumbers = smsModel.mobileAli;
|
||||
sendSmsRequest.SignName = smsModel.signName;
|
||||
sendSmsRequest.TemplateCode = smsModel.templateId;
|
||||
sendSmsRequest.TemplateParam = smsModel.templateParamAli;
|
||||
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsResponse sendSmsResponse = client.SendSms(sendSmsRequest);
|
||||
if (sendSmsResponse.Body.Code.Equals("OK") && sendSmsResponse.Body.Message.Equals("OK"))
|
||||
{
|
||||
return sendSmsResponse.Body.RequestId;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(sendSmsResponse.Body.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模板配置字段.
|
||||
/// </summary>
|
||||
/// <param name="smsModel"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string> GetTemplateByAli(SmsParameterInfo smsModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = new Config
|
||||
{
|
||||
// 您的AccessKey ID
|
||||
AccessKeyId = smsModel.keyId,
|
||||
|
||||
// 您的AccessKey Secret
|
||||
AccessKeySecret = smsModel.keySecret,
|
||||
};
|
||||
config.Endpoint = smsModel.domain;
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Client client = new AlibabaCloud.SDK.Dysmsapi20170525.Client(config);
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Models.QuerySmsTemplateRequest querySmsTemplateRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.QuerySmsTemplateRequest();
|
||||
querySmsTemplateRequest.TemplateCode = smsModel.templateId;
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Models.QuerySmsTemplateResponse querySmsTemplateResponse = client.QuerySmsTemplate(querySmsTemplateRequest);
|
||||
if (querySmsTemplateResponse.Body.Code.Equals("OK"))
|
||||
{
|
||||
return GetFields(querySmsTemplateResponse.Body.TemplateContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("获取模板失败");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
throw new Exception("获取模板失败");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 腾讯云
|
||||
|
||||
/// <summary>
|
||||
/// 腾讯云短信.
|
||||
/// </summary>
|
||||
/// <param name="smsModel"></param>
|
||||
/// <returns></returns>
|
||||
public static string SendSmsByTencent(SmsParameterInfo smsModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
Credential cred = new Credential
|
||||
{
|
||||
SecretId = smsModel.keyId,
|
||||
SecretKey = smsModel.keySecret
|
||||
};
|
||||
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.Endpoint = smsModel.domain;
|
||||
clientProfile.HttpProfile = httpProfile;
|
||||
|
||||
SmsClient client = new SmsClient(cred, smsModel.region, clientProfile);
|
||||
SendSmsRequest req = new SendSmsRequest();
|
||||
req.PhoneNumberSet = smsModel.mobileTx;
|
||||
req.SmsSdkAppId = smsModel.appId;
|
||||
req.SignName = smsModel.signName;
|
||||
req.TemplateId = smsModel.templateId;
|
||||
req.TemplateParamSet = smsModel.templateParamTx;
|
||||
SendSmsResponse resp = client.SendSmsSync(req);
|
||||
if (!resp.SendStatusSet.FirstOrDefault().Code.Equals("Ok"))
|
||||
{
|
||||
throw new Exception("短信发送失败");
|
||||
}
|
||||
return resp.RequestId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return "短信发送失败";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模板配置字段.
|
||||
/// </summary>
|
||||
/// <param name="smsModel"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string> GetTemplateByTencent(SmsParameterInfo smsModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
Credential cred = new Credential
|
||||
{
|
||||
SecretId = smsModel.keyId,
|
||||
SecretKey = smsModel.keySecret
|
||||
};
|
||||
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.Endpoint = smsModel.domain;
|
||||
clientProfile.HttpProfile = httpProfile;
|
||||
|
||||
SmsClient client = new SmsClient(cred, smsModel.region, clientProfile);
|
||||
DescribeSmsTemplateListRequest req = new DescribeSmsTemplateListRequest();
|
||||
req.International = 0;
|
||||
req.TemplateIdSet = new ulong?[] { ulong.Parse(smsModel.templateId) };
|
||||
DescribeSmsTemplateListResponse resp = client.DescribeSmsTemplateListSync(req);
|
||||
if (resp.DescribeTemplateStatusSet.Count() > 0 && !string.IsNullOrEmpty(resp.RequestId))
|
||||
{
|
||||
var data = resp.DescribeTemplateStatusSet.FirstOrDefault().ToObject<Dictionary<string, object>>();
|
||||
var templateContent = data["TemplateContent"].ToString();
|
||||
return GetFields(templateContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("获取模板失败");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new Exception("获取模板失败");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 截取{}中的字符串.
|
||||
/// </summary>
|
||||
/// <param name="templateContent"></param>
|
||||
/// <returns></returns>
|
||||
private static List<string> GetFields(string templateContent)
|
||||
{
|
||||
MatchCollection mc = Regex.Matches(templateContent, "(?i){.*?}");
|
||||
return mc.Cast<Match>().Select(m => m.Value.TrimStart('{').TrimEnd('}')).ToList();
|
||||
}
|
||||
}
|
||||
31
common/Tnb.Thirdparty/Tnb.Thirdparty.csproj
vendored
Normal file
31
common/Tnb.Thirdparty/Tnb.Thirdparty.csproj
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(SolutionDir)\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Description>JNPF 第三方组件拓展插件。</Description>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.8" />
|
||||
<PackageReference Include="DingDing.SDK.NetCore" Version="2021.1.7.1" />
|
||||
<PackageReference Include="JavaScriptEngineSwitcher.ChakraCore" Version="3.18.2" />
|
||||
<PackageReference Include="JavaScriptEngineSwitcher.ChakraCore.Native.win-x64" Version="3.18.2" />
|
||||
<PackageReference Include="JavaScriptEngineSwitcher.V8" Version="3.18.1" />
|
||||
<PackageReference Include="JavaScriptEngineSwitcher.V8.Native.linux-x64" Version="3.18.1" />
|
||||
<PackageReference Include="JavaScriptEngineSwitcher.V8.Native.win-x64" Version="3.18.1" />
|
||||
<PackageReference Include="MailKit" Version="2.15.0" />
|
||||
<PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
|
||||
<PackageReference Include="Senparc.Weixin.MP" Version="16.18.9" />
|
||||
<PackageReference Include="Senparc.Weixin.Work" Version="3.15.12" />
|
||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.368" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Tnb.Common\Tnb.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
97
common/Tnb.Thirdparty/WeChat/Internal/QYMember.cs
vendored
Normal file
97
common/Tnb.Thirdparty/WeChat/Internal/QYMember.cs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
namespace JNPF.Extras.Thirdparty.WeChat;
|
||||
|
||||
/// <summary>
|
||||
/// 企业用户.
|
||||
/// </summary>
|
||||
public sealed class QYMember
|
||||
{
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// [创建、更新 必填]成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节.
|
||||
/// </summary>
|
||||
public string userid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// [创建必填]成员名称。长度为1~64个字符.
|
||||
/// </summary>
|
||||
public string name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 英文名。长度为1-64个字节,由字母、数字、点(.)、减号(-)、空格或下划线(_)组成.
|
||||
/// </summary>
|
||||
public string english_name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 手机号码。企业内必须唯一,mobile/email二者不能同时为空.
|
||||
/// </summary>
|
||||
public string mobile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// [创建必填]成员所属部门id列表,不超过20个.
|
||||
/// </summary>
|
||||
public long[] department { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 部门内的排序值,默认为0,成员次序以创建时间从小到大排列。数量必须和department一致,数值越大排序越前面。有效的值范围是[0, 2^32).
|
||||
/// </summary>
|
||||
public long[] order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 职位信息。长度为0~128个字符.
|
||||
/// </summary>
|
||||
public string position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 性别。1表示男性,2表示女性.
|
||||
/// </summary>
|
||||
public string gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 邮箱。长度不超过64个字节,且为有效的email格式。企业内必须唯一,mobile/email二者不能同时为空.
|
||||
/// </summary>
|
||||
public string email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 上级字段,标识是否为上级。在审批等应用里可以用来标识上级审批人.
|
||||
/// </summary>
|
||||
public int isleader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 启用/禁用成员。1表示启用成员,0表示禁用成员.
|
||||
/// </summary>
|
||||
public int enable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 成员头像的mediaid,通过素材管理接口上传图片获得的mediaid.
|
||||
/// </summary>
|
||||
public string avatar_mediaid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 座机。由1-32位的纯数字或’-‘号组成.
|
||||
/// </summary>
|
||||
public string telephone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 自定义字段。自定义字段需要先在WEB管理端添加,见扩展属性添加方法,否则忽略未知属性的赋值。自定义字段长度为0~32个字符,超过将被截断.
|
||||
/// </summary>
|
||||
public object extattr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摘要:
|
||||
/// 成员对外属性,字段详情见对外属性: http://work.weixin.qq.com/api/doc#13450.
|
||||
/// </summary>
|
||||
public object external_profile { get; set; }
|
||||
}
|
||||
52
common/Tnb.Thirdparty/WeChat/Internal/WechatMPEvent.cs
vendored
Normal file
52
common/Tnb.Thirdparty/WeChat/Internal/WechatMPEvent.cs
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace JNPF.Extras.Thirdparty.WeChat.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// 公众号事件.
|
||||
/// </summary>
|
||||
public class WechatMPEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 签名.
|
||||
/// </summary>
|
||||
public string signature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间戳.
|
||||
/// </summary>
|
||||
public string timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// nonce.
|
||||
/// </summary>
|
||||
public string nonce { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 算出的值.
|
||||
/// </summary>
|
||||
public string echostr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开发者微信号.
|
||||
/// </summary>
|
||||
public string ToUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发送方帐号(一个OpenID).
|
||||
/// </summary>
|
||||
public string FromUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息创建时间 (整型).
|
||||
/// </summary>
|
||||
public long CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息类型,event.
|
||||
/// </summary>
|
||||
public string MsgType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件类型,subscribe(订阅)、unsubscribe(取消订阅).
|
||||
/// </summary>
|
||||
public string Event { get; set; }
|
||||
}
|
||||
150
common/Tnb.Thirdparty/WeChat/WeChatMPUtil.cs
vendored
Normal file
150
common/Tnb.Thirdparty/WeChat/WeChatMPUtil.cs
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
using Senparc.Weixin.MP;
|
||||
using Senparc.Weixin.MP.AdvancedAPIs;
|
||||
using Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage;
|
||||
using Senparc.Weixin.MP.AdvancedAPIs.User;
|
||||
using Senparc.Weixin.MP.Containers;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.WeChat;
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号.
|
||||
/// </summary>
|
||||
public class WeChatMPUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 访问令牌.
|
||||
/// </summary>
|
||||
public string accessToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数.
|
||||
/// </summary>
|
||||
public WeChatMPUtil() { }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数.
|
||||
/// </summary>
|
||||
public WeChatMPUtil(string appId, string appSecret)
|
||||
{
|
||||
try
|
||||
{
|
||||
accessToken = AccessTokenContainer.TryGetAccessToken(appId, appSecret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信后台验证地址.
|
||||
/// </summary>
|
||||
/// <param name="signature">签名.</param>
|
||||
/// <param name="timestamp">时间戳.</param>
|
||||
/// <param name="nonce">nonce.</param>
|
||||
/// <param name="echostr">算出的值.</param>
|
||||
/// <returns></returns>
|
||||
public bool CheckToken(string signature, string timestamp, string nonce)
|
||||
{
|
||||
var token = "WEIXINJNPF";
|
||||
return CheckSignature.Check(signature, timestamp, nonce, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据OpenId进行群发.
|
||||
/// </summary>
|
||||
/// <param name="type">消息类型.</param>
|
||||
/// <param name="value">群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId.</param>
|
||||
/// <param name="clientmsgid">开发者侧群发msgid,长度限制64字节,如不填,则后台默认以群发范围和群发内容的摘要值做为clientmsgid.</param>
|
||||
/// <param name="openIds"> openId字符串数组.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool SendGroupMessageByOpenId(int type, string value, string[] openIds, string clientmsgid = null, int timeOut = 10000)
|
||||
{
|
||||
try
|
||||
{
|
||||
GroupMessageType messageType = new GroupMessageType();
|
||||
switch (type)
|
||||
{
|
||||
case 1://文本
|
||||
messageType = GroupMessageType.text;
|
||||
break;
|
||||
case 2://图片
|
||||
messageType = GroupMessageType.image;
|
||||
break;
|
||||
case 3://语音
|
||||
messageType = GroupMessageType.voice;
|
||||
break;
|
||||
case 4://视频
|
||||
messageType = GroupMessageType.video;
|
||||
break;
|
||||
case 5://图文
|
||||
messageType = GroupMessageType.mpnews;
|
||||
break;
|
||||
case 6:// 卡券
|
||||
messageType = GroupMessageType.wxcard;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
var result = GroupMessageApi.SendGroupMessageByOpenId(accessToken, messageType, value, clientmsgid, timeOut, openIds);
|
||||
return result.errcode == 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据OpenId发送模板消息.
|
||||
/// </summary>
|
||||
/// <param name="openId"></param>
|
||||
/// <param name="templateId"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public void SendTemplateMessage(string openId, string templateId, string url, object data, TemplateModel_MiniProgram miniProgram = null)
|
||||
{
|
||||
var result = TemplateApi.SendTemplateMessage(accessToken, openId, templateId, url, data, miniProgram);
|
||||
if (result.errcode != 0)
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据OpenId发送模板消息.
|
||||
/// </summary>
|
||||
/// <param name="openId"></param>
|
||||
/// <param name="templateId"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public string GetTemplateMp(string templateId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = TemplateApi.GetPrivateTemplate(accessToken);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
var data = result.template_list.Find(x => x.template_id == templateId);
|
||||
return data.content;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过openId获取用户信息.
|
||||
/// </summary>
|
||||
/// <param name="openId"></param>
|
||||
/// <returns></returns>
|
||||
public UserInfoJson GetWeChatUserInfo(string openId)
|
||||
{
|
||||
return UserApi.Info(accessToken, openId);
|
||||
}
|
||||
}
|
||||
450
common/Tnb.Thirdparty/WeChat/WeChatUtil.cs
vendored
Normal file
450
common/Tnb.Thirdparty/WeChat/WeChatUtil.cs
vendored
Normal file
@@ -0,0 +1,450 @@
|
||||
using System.Text.Json;
|
||||
using Senparc.Weixin.Work.AdvancedAPIs;
|
||||
using Senparc.Weixin.Work.AdvancedAPIs.MailList;
|
||||
using Senparc.Weixin.Work.AdvancedAPIs.MailList.Member;
|
||||
using Senparc.Weixin.Work.Containers;
|
||||
|
||||
namespace JNPF.Extras.Thirdparty.WeChat;
|
||||
|
||||
/// <summary>
|
||||
/// 企业号微信.
|
||||
/// </summary>
|
||||
public class WeChatUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 访问令牌.
|
||||
/// </summary>
|
||||
public string accessToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数.
|
||||
/// </summary>
|
||||
public WeChatUtil(string corpId, string corpSecret, string appSecret = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (appSecret != null)
|
||||
corpSecret = appSecret;
|
||||
accessToken = AccessTokenContainer.TryGetToken(corpId, corpSecret);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#region 部门
|
||||
|
||||
/// <summary>
|
||||
/// 查询部门.
|
||||
/// </summary>
|
||||
public List<DepartmentList> GetDepartmentList()
|
||||
{
|
||||
var result = MailListApi.GetDepartmentList(accessToken);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return result.department;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建部门.
|
||||
/// </summary>
|
||||
/// <param name="name">部门名称.</param>
|
||||
/// <param name="parentId">父亲部门id.</param>
|
||||
/// <param name="order">在父部门中的次序.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <param name="id">部门id.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public string CreateDepartment(string name, long parentId, int order, ref string msg, int? id = null, int timeOut = 10000)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MailListApi.CreateDepartment(accessToken, name, parentId);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return result.id.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = result.errmsg;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = string.Format("【组织{0}创建失败】{1}", name, ex.Message);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑部门.
|
||||
/// </summary>
|
||||
/// <param name="id">部门Id.</param>
|
||||
/// <param name="name">部门名称.</param>
|
||||
/// <param name="parentId">父亲部门id.</param>
|
||||
/// <param name="order">在父部门中的次序.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool UpdateDepartment(int? id, string name, int? parentId, int? order, ref string msg, int timeOut = 10000)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MailListApi.UpdateDepartment(accessToken, Convert.ToInt64(id), name);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = result.errmsg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = string.Format("【组织{0}修改失败】{1}", name, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除部门.
|
||||
/// </summary>
|
||||
/// <param name="id">部门Id.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <returns></returns>
|
||||
public bool DeleteDepartment(int? id, ref string msg)
|
||||
{
|
||||
var result = MailListApi.DeleteDepartment(accessToken, Convert.ToInt64(id));
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = result.errmsg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取部门成员.
|
||||
/// </summary>
|
||||
/// <param name="departmentId">获取的部门id.</param>
|
||||
/// <param name="fetchChild">1/0:是否递归获取子部门下面的成员.</param>
|
||||
/// <returns></returns>
|
||||
public List<GetMemberResult> GetDepartmentMember()
|
||||
{
|
||||
var depList = GetDepartmentList();
|
||||
var userList = new List<GetMemberResult>();
|
||||
if (depList.Count > 0)
|
||||
{
|
||||
foreach (var item in depList.Select(x => x.id))
|
||||
{
|
||||
var result = MailListApi.GetDepartmentMemberInfo(accessToken, item, 1);
|
||||
if (result.errcode == 0 && result.userlist.Count > 0)
|
||||
{
|
||||
userList = userList.Union(result.userlist).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
return userList;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 用户
|
||||
|
||||
/// <summary>
|
||||
/// 获取成员.
|
||||
/// </summary>
|
||||
/// <param name="userId">用户Id.</param>
|
||||
/// <returns></returns>
|
||||
public string GetMember(string userId)
|
||||
{
|
||||
var result = MailListApi.GetMember(accessToken, userId);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return JsonSerializer.Serialize(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建成员.
|
||||
/// </summary>
|
||||
/// <param name="member">企业用户.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
public bool CreateMember(QYMember member, ref string msg, int timeOut = 10000)
|
||||
{
|
||||
try
|
||||
{
|
||||
MemberCreateRequest memberCreate = new MemberCreateRequest();
|
||||
memberCreate.name = member.name;
|
||||
memberCreate.avatar_mediaid = member.avatar_mediaid;
|
||||
memberCreate.department = member.department;
|
||||
memberCreate.email = member.email;
|
||||
memberCreate.enable = member.enable;
|
||||
memberCreate.english_name = member.english_name;
|
||||
memberCreate.extattr = (Extattr)member.extattr;
|
||||
memberCreate.external_profile = (External_Profile)member.external_profile;
|
||||
memberCreate.gender = member.gender;
|
||||
memberCreate.mobile = member.mobile;
|
||||
memberCreate.order = member.order;
|
||||
memberCreate.position = member.position;
|
||||
memberCreate.telephone = member.telephone;
|
||||
memberCreate.userid = member.userid;
|
||||
var result = MailListApi.CreateMember(accessToken, memberCreate, timeOut);
|
||||
if (result.errcode == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑成员.
|
||||
/// </summary>
|
||||
/// <param name="member">企业用户.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
public bool UpdateMember(QYMember member, ref string msg, int timeOut = 10000)
|
||||
{
|
||||
try
|
||||
{
|
||||
MemberUpdateRequest memberUpdate = new MemberUpdateRequest();
|
||||
memberUpdate.name = member.name;
|
||||
memberUpdate.avatar_mediaid = member.avatar_mediaid;
|
||||
memberUpdate.department = member.department;
|
||||
memberUpdate.email = member.email;
|
||||
memberUpdate.enable = member.enable;
|
||||
memberUpdate.english_name = member.english_name;
|
||||
memberUpdate.extattr = (Extattr)member.extattr;
|
||||
memberUpdate.external_profile = (External_Profile)member.external_profile;
|
||||
memberUpdate.gender = member.gender;
|
||||
memberUpdate.mobile = member.mobile;
|
||||
memberUpdate.order = member.order;
|
||||
memberUpdate.position = member.position;
|
||||
memberUpdate.telephone = member.telephone;
|
||||
memberUpdate.userid = member.userid;
|
||||
var result = MailListApi.UpdateMember(accessToken, memberUpdate, timeOut);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除成员.
|
||||
/// </summary>
|
||||
/// <param name="userId">成员Id.</param>
|
||||
/// <param name="msg">返回信息.</param>
|
||||
/// <returns></returns>
|
||||
public bool DeleteMember(string userId, string msg = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MailListApi.DeleteMember(accessToken, userId);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除成员.
|
||||
/// </summary>
|
||||
/// <param name="useridlist">成员UserID列表.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool BatchDeleteMember(string[] useridlist, int timeOut = 10000)
|
||||
{
|
||||
var result = MailListApi.BatchDeleteMember(accessToken, useridlist, timeOut = 10000);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手机号获取成员.
|
||||
/// </summary>
|
||||
/// <param name="mobile">手机号码.</param>
|
||||
public string GetUserid(string mobile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MailListApi.GetUserid(accessToken, mobile, 10000);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return result.userid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 消息
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息.
|
||||
/// </summary>
|
||||
/// <param name="agentId">企业应用的id.</param>
|
||||
/// <param name="content">消息内容.</param>
|
||||
/// <param name="toUser">UserID列表.</param>
|
||||
/// <param name="toParty">PartyID列表.</param>
|
||||
/// <param name="toTag">TagID列表.</param>
|
||||
/// <param name="safe">否是保密消息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public async Task SendText(string agentId, string content, string toUser, string toParty = null, string toTag = null, int safe = 0, int timeOut = 10000)
|
||||
{
|
||||
var result = await MassApi.SendTextAsync(accessToken, agentId, content, toUser, toParty, toTag, safe);
|
||||
if (result.errcode != 0)
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送文件消息.
|
||||
/// </summary>
|
||||
/// <param name="agentId">企业应用的id.</param>
|
||||
/// <param name="mediaId">媒体资源文件ID.</param>
|
||||
/// <param name="toUser">UserID列表.</param>
|
||||
/// <param name="toParty">PartyID列表.</param>
|
||||
/// <param name="toTag">TagID列表.</param>
|
||||
/// <param name="safe">否是保密消息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool SendFile(string agentId, string mediaId, string toUser, string toParty = null, string toTag = null, int safe = 0, int timeOut = 10000)
|
||||
{
|
||||
var result = MassApi.SendFile(accessToken, agentId, mediaId, toUser, toParty, toTag, safe);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送图片消息.
|
||||
/// </summary>
|
||||
/// <param name="agentId">企业应用的id.</param>
|
||||
/// <param name="mediaId">媒体资源文件ID.</param>
|
||||
/// <param name="toUser">UserID列表.</param>
|
||||
/// <param name="toParty">PartyID列表.</param>
|
||||
/// <param name="toTag">TagID列表.</param>
|
||||
/// <param name="safe">否是保密消息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
public bool SendImage(string agentId, string mediaId, string toUser, string toParty = null, string toTag = null, int safe = 0, int timeOut = 10000)
|
||||
{
|
||||
var result = MassApi.SendImage(accessToken, agentId, mediaId, toUser, toParty, toTag, safe);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送视频消息.
|
||||
/// </summary>
|
||||
/// <param name="agentId">企业应用的id.</param>
|
||||
/// <param name="mediaId">媒体资源文件ID.</param>
|
||||
/// <param name="toUser">UserID列表.</param>
|
||||
/// <param name="toParty">PartyID列表.</param>
|
||||
/// <param name="toTag">TagID列表.</param>
|
||||
/// <param name="title"> 视频消息的标题.</param>
|
||||
/// <param name="description">视频消息的描述.</param>
|
||||
/// <param name="safe">否是保密消息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool SendVideo(string agentId, string mediaId, string toUser, string toParty = null, string toTag = null, string title = null, string description = null, int safe = 0, int timeOut = 10000)
|
||||
{
|
||||
var result = MassApi.SendVideo(accessToken, agentId, mediaId, toUser, toParty, toTag, title, description, safe);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送语音消息.
|
||||
/// </summary>
|
||||
/// <param name="agentId">企业应用的id.</param>
|
||||
/// <param name="mediaId">媒体资源文件ID.</param>
|
||||
/// <param name="toUser">UserID列表.</param>
|
||||
/// <param name="toParty">PartyID列表.</param>
|
||||
/// <param name="toTag">TagID列表.</param>
|
||||
/// <param name="safe">否是保密消息.</param>
|
||||
/// <param name="timeOut">代理请求超时时间(毫秒).</param>
|
||||
/// <returns></returns>
|
||||
public bool SendVoice(string agentId, string mediaId, string toUser, string toParty = null, string toTag = null, int safe = 0, int timeOut = 10000)
|
||||
{
|
||||
var result = MassApi.SendVoice(accessToken, agentId, mediaId, toUser, toParty, toTag, safe);
|
||||
if (result.errcode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user