mirror of
http://git.coreshop.cn/jianweie/coreshoppro.git
synced 2026-02-05 10:19:48 +08:00
【新增】增加MySql.Data组件,防止出现Nlog新版mysql不写入的问题。
【优化】优化本地上传写入文件夹缺少反斜杠的问题。 【修复】修复appsetting.json微信公众号配置项命名错误的问题。 【新增】定时任务增加微信公众号获取JS-Token并且全局缓存功能。 【新增】增加微信公众号获取JS-SDK使用权限签名算法。 【新增】新增微信公众号【微信被动回复消息】功能,用于关于公众号提醒,微信聊天被动关键词回复等。 【新增】新增微信公众号【微信菜单管理】功能,用于管理公众号底部三列五横的链接。 【新增】新增微信公众号【消息推送】接口及业务处理。用于接收从微信公众号推送的事件交互。 【新增】新增微信公众号【授权鉴权跳转】功能,用于验证是否为微信公众号访问,及获取openid等信息。 【新增】新增微信公众号用户accressToekn存储机制。 【新增】新增微信公众号发送模板消息方法。 【新增】数据库新增【WeChatMessageResponse】【WeChatUserAccessToken】两个表,【CoreCmsUserWeChatInfo】增加【isSubscribe】是否关注字段。 【新增】商品品牌管理品牌logo增加原图上传 【新增】商品分类管理分类图片增加原图上传 【新增】新增最新后台左侧管理菜单目录脚本,完整数据库脚本备份新增最新商品商品示例。
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
using System;
|
||||
using CoreCms.Net.WeChat.Service.HttpClients;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api.Events;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using CoreCms.Net.Caching.AccressToken;
|
||||
using CoreCms.Net.Configuration;
|
||||
using CoreCms.Net.IServices;
|
||||
using CoreCms.Net.Loging;
|
||||
using CoreCms.Net.Model.Entities;
|
||||
using CoreCms.Net.Utility.Helper;
|
||||
using CoreCms.Net.WeChat.Service.Configuration;
|
||||
using CoreCms.Net.WeChat.Service.Mediator;
|
||||
using CoreCms.Net.WeChat.Service.Models;
|
||||
using CoreCms.Net.WeChat.Service.Options;
|
||||
using CoreCms.Net.WeChat.Service.Utilities;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api.Models;
|
||||
using LogLevel = NLog.LogLevel;
|
||||
using CoreCms.Net.Services;
|
||||
using SqlSugar;
|
||||
|
||||
namespace CoreCms.Net.Web.Controllers.WeChat
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号消息推送对接
|
||||
/// </summary>
|
||||
public class WeChatOffiaccountNotifyController : Controller
|
||||
{
|
||||
private readonly IWeChatApiHttpClientFactory _weChatApiHttpClientFactory;
|
||||
private readonly WeChatOptions _weChatOptions;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
private readonly ICoreCmsUserWeChatInfoServices _weChatUserInfoServices;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 原始的加密请求(如果不加密则为null)
|
||||
/// </summary>
|
||||
public XDocument? EcryptRequestDocument { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用加密
|
||||
/// </summary>
|
||||
public bool UsingEncryptMessage = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否取消执行
|
||||
/// </summary>
|
||||
public bool CancelExecute = false;
|
||||
/// <summary>
|
||||
/// 是否使用兼容模式
|
||||
/// </summary>
|
||||
public bool UsingCompatibilityModelEncryptMessage = false;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public WeChatOffiaccountNotifyController(IWeChatApiHttpClientFactory weChatApiHttpClientFactory, IOptions<WeChatOptions> weChatOptions, IMediator mediator, ICoreCmsUserWeChatInfoServices weChatUserInfoServices)
|
||||
{
|
||||
_weChatApiHttpClientFactory = weChatApiHttpClientFactory;
|
||||
_mediator = mediator;
|
||||
_weChatUserInfoServices = weChatUserInfoServices;
|
||||
_weChatOptions = weChatOptions.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET请求用于处理微信公众号后台的URL验证
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[ActionName("Index")]
|
||||
public ActionResult Get([FromQuery(Name = "timestamp")] string timestamp,
|
||||
[FromQuery(Name = "nonce")] string nonce,
|
||||
[FromQuery(Name = "signature")] string signature,
|
||||
[FromQuery(Name = "echostr")] string echoString)
|
||||
{
|
||||
// 验证服务器推送
|
||||
// 文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
|
||||
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "GET接收到微信推送的数据", JsonConvert.SerializeObject(new
|
||||
{
|
||||
timestamp,
|
||||
nonce,
|
||||
signature,
|
||||
echoString
|
||||
}));
|
||||
|
||||
var client = _weChatApiHttpClientFactory.CreateWeXinClient();
|
||||
var valid = client.VerifyEventSignatureForEcho(callbackTimestamp: timestamp, callbackNonce: nonce, callbackSignature: signature);
|
||||
|
||||
return Content(!valid ? "fail" : echoString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 接收服务器推送
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[ActionName("Index")]
|
||||
public async Task<IActionResult> Post(PostModel postModel)
|
||||
{
|
||||
// 接收服务器推送
|
||||
// 文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
|
||||
|
||||
if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, _weChatOptions.WeiXinToken))
|
||||
{
|
||||
NLogUtil.WriteFileLog(LogLevel.Error, LogType.WeChat, "Post接收服务器推送(签名错误)", JsonConvert.SerializeObject(postModel));
|
||||
return Content("fail");
|
||||
}
|
||||
else
|
||||
{
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "Post接收服务器推送(签名成功)", JsonConvert.SerializeObject(postModel));
|
||||
}
|
||||
|
||||
postModel.Token = _weChatOptions.WeiXinToken;//根据自己后台的设置保持一致
|
||||
postModel.EncodingAESKey = _weChatOptions.WeiXinEncodingAesKey;//根据自己后台的设置保持一致
|
||||
postModel.AppId = _weChatOptions.WeiXinAppId;//根据自己后台的设置保持一致(必须提供)
|
||||
|
||||
//获取流数据转xml流
|
||||
XDocument postDataDocument = XmlUtility.Convert(Request.GetRequestStream());
|
||||
|
||||
var msgXml = string.Empty;
|
||||
var callbackXml = Init(postDataDocument, postModel, ref msgXml);
|
||||
|
||||
//怕出现误判,所以将最优结果判断
|
||||
if (callbackXml != null && CancelExecute == false && !string.IsNullOrEmpty(msgXml))
|
||||
{
|
||||
/* 如果是 XML 格式的通知内容 */
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(XML格式的通知内容)", JsonConvert.SerializeObject(callbackXml));
|
||||
var callBack = await ExecuteProcess(callbackXml, msgXml);
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(XML通知微信服务器)", callBack.Data);
|
||||
return Content(callBack.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(解密失败)", JsonConvert.SerializeObject(callbackXml));
|
||||
return Content("fail");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 处理xml内容
|
||||
/// <summary>
|
||||
/// 对解密后的xml数据进行筛选并分发处理结果
|
||||
/// </summary>
|
||||
public async Task<WeChatApiCallBack> ExecuteProcess(XDocument sourceXml, string msgXml)
|
||||
{
|
||||
//被动回复消息
|
||||
//文件:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html
|
||||
var requestType = sourceXml.Root?.Element("MsgType")?.Value;
|
||||
|
||||
WeChatApiCallBack callBack = new WeChatApiCallBack();
|
||||
|
||||
if (!string.IsNullOrEmpty(requestType))
|
||||
{
|
||||
var client = _weChatApiHttpClientFactory.CreateWeXinClient();
|
||||
|
||||
switch (requestType)
|
||||
{
|
||||
case RequestMsgType.Text:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<TextMessageEvent>(msgXml);
|
||||
var replyModel = new TextMessageReply()
|
||||
{
|
||||
ToUserName = eventModel.FromUserName,
|
||||
FromUserName = eventModel.ToUserName,
|
||||
CreateTimestamp = CommonHelper.GetTimeStampByTotalSeconds(),
|
||||
Content = "您发送的消息是" + eventModel.Content
|
||||
};
|
||||
var replyXml = client.SerializeEventToXml(replyModel, false);
|
||||
callBack.Data = replyXml;
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.Location:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<LocationMessageEvent>(msgXml);
|
||||
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.Image:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<ImageMessageEvent>(msgXml);
|
||||
|
||||
var replyModel = new ImageMessageReply()
|
||||
{
|
||||
ToUserName = eventModel.FromUserName,
|
||||
FromUserName = eventModel.ToUserName,
|
||||
CreateTimestamp = CommonHelper.GetTimeStampByTotalSeconds(),
|
||||
|
||||
};
|
||||
var replyXml = client.SerializeEventToXml(replyModel, false);
|
||||
callBack.Data = replyXml;
|
||||
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.Voice:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<VoiceMessageEvent>(msgXml);
|
||||
|
||||
var replyModel = new VoiceMessageReply()
|
||||
{
|
||||
ToUserName = eventModel.FromUserName,
|
||||
FromUserName = eventModel.ToUserName,
|
||||
CreateTimestamp = CommonHelper.GetTimeStampByTotalSeconds(),
|
||||
|
||||
};
|
||||
var replyXml = client.SerializeEventToXml(replyModel, false);
|
||||
callBack.Data = replyXml;
|
||||
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.Video:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<VideoMessageEvent>(msgXml);
|
||||
|
||||
var replyModel = new VideoMessageReply()
|
||||
{
|
||||
ToUserName = eventModel.FromUserName,
|
||||
FromUserName = eventModel.ToUserName,
|
||||
CreateTimestamp = CommonHelper.GetTimeStampByTotalSeconds(),
|
||||
|
||||
};
|
||||
var replyXml = client.SerializeEventToXml(replyModel, false);
|
||||
callBack.Data = replyXml;
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.ShortVideo:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<ShortVideoMessageEvent>(msgXml);
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.Link:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<VoiceMessageEvent>(msgXml);
|
||||
|
||||
}
|
||||
break;
|
||||
case RequestMsgType.MessageEvent:
|
||||
{
|
||||
var eventType = sourceXml.Root?.Element("Event")?.Value;
|
||||
if (!string.IsNullOrEmpty(eventType))
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
//订阅(关注)事件
|
||||
case EventType.Subscribe:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<SubscribePushEvent>(msgXml);
|
||||
var accessToken = WeChatCacheAccessTokenHelper.GetWeChatAccessToken();
|
||||
|
||||
if (eventModel.FromUserName != null)
|
||||
{
|
||||
var request = new CgibinUserInfoRequest() { AccessToken = accessToken, OpenId = eventModel.FromUserName };
|
||||
var response = await client.ExecuteCgibinUserInfoAsync(request, cancellationToken: HttpContext.RequestAborted);
|
||||
if (!response.IsSuccessful())
|
||||
{
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "获取用户基本信息失败", @"获取用户基本信息失败(状态码:{response.RawStatus},错误代码:{response.ErrorCode},错误描述:{ response.ErrorMessage})。");
|
||||
}
|
||||
else
|
||||
{
|
||||
var userInfo = await _weChatUserInfoServices.QueryByClauseAsync(p => p.openid == eventModel.FromUserName);
|
||||
if (userInfo == null)
|
||||
{
|
||||
|
||||
userInfo = new Model.Entities.CoreCmsUserWeChatInfo()
|
||||
{
|
||||
isSubscribe = response.IsSubscribed,
|
||||
openid = response.OpenId,
|
||||
language = response.Language,
|
||||
//createTime = response.SubscribeTimestamp,
|
||||
type = (int)GlobalEnumVars.UserAccountTypes.微信公众号,
|
||||
//sessionKey = response.s,
|
||||
gender = 1,
|
||||
createTime = DateTime.Now,
|
||||
unionId = response.UnionId,
|
||||
};
|
||||
var id = await _weChatUserInfoServices.InsertAsync(userInfo);
|
||||
if (id > 0)
|
||||
{
|
||||
await _weChatUserInfoServices.UpdateAsync(
|
||||
p => new Model.Entities.CoreCmsUserWeChatInfo()
|
||||
{
|
||||
userId = id
|
||||
}, p => p.id == id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userInfo.isSubscribe = response.IsSubscribed;
|
||||
userInfo.unionId = response.UnionId;
|
||||
userInfo.updateTime = DateTime.Now;
|
||||
|
||||
await _weChatUserInfoServices.UpdateAsync(userInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// 退订
|
||||
// 实际上用户无法收到非订阅账号的消息,所以这里可以随便写。
|
||||
// unsubscribe事件的意义在于及时删除网站应用中已经记录的OpenID绑定,消除冗余数据。并且关注用户流失的情况。
|
||||
case EventType.Unsubscribe:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case EventType.Localtion:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case EventType.Click:
|
||||
{
|
||||
var eventModel = client.DeserializeEventFromXml<ClickPushEvent>(msgXml);
|
||||
|
||||
var replyModel = new TextMessageReply()
|
||||
{
|
||||
ToUserName = eventModel.FromUserName,
|
||||
FromUserName = eventModel.ToUserName,
|
||||
CreateTimestamp = CommonHelper.GetTimeStampByTotalSeconds(),
|
||||
Content = "您刚才发送了ENTER事件请求"
|
||||
};
|
||||
var replyXml = client.SerializeEventToXml(replyModel, false);
|
||||
callBack.Data = replyXml;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(处理xml内容/Event无匹配)", JsonConvert.SerializeObject(sourceXml));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(处理xml内容/MsgType无匹配)", JsonConvert.SerializeObject(sourceXml));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "接收服务器推送(处理xml内容/获取MsgType失败)", JsonConvert.SerializeObject(sourceXml));
|
||||
}
|
||||
|
||||
return callBack;
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 初始化获取xml文本数据
|
||||
|
||||
/// <summary>
|
||||
/// 初始化获取xml文本数据
|
||||
/// </summary>
|
||||
/// <param name="postDataDocument"></param>
|
||||
/// <param name="postModel"></param>
|
||||
/// <param name="msgXml"></param>
|
||||
/// <returns></returns>
|
||||
private XDocument? Init(XDocument postDataDocument, PostModel postModel, ref string msgXml)
|
||||
{
|
||||
//进行加密判断并处理
|
||||
var postDataStr = postDataDocument.ToString();
|
||||
XDocument decryptDoc = postDataDocument;
|
||||
if (postDataDocument.Root?.Element("Encrypt") != null && !string.IsNullOrEmpty(postDataDocument.Root.Element("Encrypt")?.Value))
|
||||
{
|
||||
//使用了加密
|
||||
UsingEncryptMessage = true;
|
||||
EcryptRequestDocument = postDataDocument;
|
||||
|
||||
WXBizMsgCrypt msgCrype = new WXBizMsgCrypt(postModel.Token, postModel.EncodingAESKey, postModel.AppId);
|
||||
|
||||
var result = msgCrype.DecryptMsg(postModel.Msg_Signature, postModel.Timestamp, postModel.Nonce, postDataStr, ref msgXml);
|
||||
//判断result类型
|
||||
if (result != 0)
|
||||
{
|
||||
//验证没有通过,取消执行
|
||||
CancelExecute = true;
|
||||
return null;
|
||||
}
|
||||
if (postDataDocument.Root.Element("FromUserName") != null && !string.IsNullOrEmpty(postDataDocument.Root.Element("FromUserName")?.Value))
|
||||
{
|
||||
//TODO:使用了兼容模式,进行验证即可
|
||||
UsingCompatibilityModelEncryptMessage = true;
|
||||
}
|
||||
decryptDoc = XDocument.Parse(msgXml);//完成解密
|
||||
}
|
||||
return decryptDoc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CoreCms.Net.Configuration;
|
||||
using CoreCms.Net.Caching.AccressToken;
|
||||
using CoreCms.Net.IServices;
|
||||
using CoreCms.Net.Loging;
|
||||
using CoreCms.Net.Model.Entities;
|
||||
using CoreCms.Net.WeChat.Service.HttpClients;
|
||||
using CoreCms.Net.WeChat.Service.Enums;
|
||||
using CoreCms.Net.WeChat.Service.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api.Models;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api;
|
||||
using LogLevel = NLog.LogLevel;
|
||||
|
||||
namespace CoreCms.Net.Web.Controllers.WeChat
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信公众号用户授权事件
|
||||
/// </summary>
|
||||
public class WeChatOffiaccountOAuth2Controller : Controller
|
||||
{
|
||||
//private readonly string _weChatAppId = AppSettingsConstVars.WeiXinAppId;
|
||||
//private readonly string _weChatSecret = AppSettingsConstVars.WeiXinAppSecret;
|
||||
//private readonly string _weChatOAuth2CallBackUrl = AppSettingsConstVars.AppConfigAppInterFaceUrl + "/WeCharOAuth2/UserInfoCallback";
|
||||
|
||||
private readonly IWeChatApiHttpClientFactory _weChatApiHttpClientFactory;
|
||||
private readonly IWeChatUserAccessTokenServices _weChatUserAccessTokenServices;
|
||||
private readonly ICoreCmsUserWeChatInfoServices _weChatUserInfoServices;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public WeChatOffiaccountOAuth2Controller(IWeChatApiHttpClientFactory weChatApiHttpClientFactory, IWeChatUserAccessTokenServices weChatUserAccessTokenServices, ICoreCmsUserWeChatInfoServices weChatUserInfoServices)
|
||||
{
|
||||
_weChatApiHttpClientFactory = weChatApiHttpClientFactory;
|
||||
_weChatUserAccessTokenServices = weChatUserAccessTokenServices;
|
||||
_weChatUserInfoServices = weChatUserInfoServices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OAuthScope.snsapi_userinfo方式回调
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="bkUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ActionResult> UserInfoCallback(string code, string state, string bkUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
return Content("您拒绝了授权!");
|
||||
}
|
||||
|
||||
//if (!state.Contains(","))
|
||||
//{
|
||||
// //这里的state其实是会暴露给客户端的,验证能力很弱,这里只是演示一下
|
||||
// //实际上可以存任何想传递的数据,比如用户ID,并且需要结合例如下面的Session["OAuthAccessToken"]进行验证
|
||||
// return Content("验证失败!请从正规途径进入!");
|
||||
//}
|
||||
|
||||
var client = _weChatApiHttpClientFactory.CreateWeXinClient();
|
||||
var accessToken = WeChatCacheAccessTokenHelper.GetWeChatAccessToken();
|
||||
|
||||
var request = new SnsOAuth2AccessTokenRequest()
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
Code = code
|
||||
};
|
||||
|
||||
var response = await client.ExecuteSnsOAuth2AccessTokenAsync(request, HttpContext.RequestAborted);
|
||||
if (response.ErrorCode != (int)WeChatReturnCode.ReturnCode.请求成功)
|
||||
{
|
||||
return Content("错误:" + response.ErrorMessage);
|
||||
}
|
||||
|
||||
NLogUtil.WriteFileLog(LogLevel.Info, LogType.WeChat, "获取用户accessToken", JsonConvert.SerializeObject(response));
|
||||
|
||||
var accessTokenModel = await _weChatUserAccessTokenServices.QueryByClauseAsync(p => p.openid == response.OpenId);
|
||||
if (accessTokenModel == null)
|
||||
{
|
||||
accessTokenModel = new WeChatUserAccessToken();
|
||||
accessTokenModel.access_token = response.AccessToken;
|
||||
accessTokenModel.expires_in = response.ExpiresIn;
|
||||
accessTokenModel.openid = response.OpenId;
|
||||
accessTokenModel.refresh_token = response.RefreshToken;
|
||||
accessTokenModel.scope = response.Scope;
|
||||
accessTokenModel.unionid = response.UnionId;
|
||||
accessTokenModel.refresh_DateTime = DateTime.Now.AddSeconds(response.ExpiresIn);
|
||||
await _weChatUserAccessTokenServices.InsertAsync(accessTokenModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
accessTokenModel.access_token = response.AccessToken;
|
||||
accessTokenModel.expires_in = response.ExpiresIn;
|
||||
//accessTokenModel.openid = response.OpenId;
|
||||
accessTokenModel.refresh_token = response.RefreshToken;
|
||||
accessTokenModel.scope = response.Scope;
|
||||
accessTokenModel.unionid = response.UnionId;
|
||||
accessTokenModel.refresh_DateTime = DateTime.Now.AddSeconds(response.ExpiresIn);
|
||||
await _weChatUserAccessTokenServices.UpdateAsync(accessTokenModel);
|
||||
}
|
||||
|
||||
//因为第一步选择的是OAuthScope.snsapi_userinfo,这里可以进一步获取用户详细信息
|
||||
try
|
||||
{
|
||||
var userInfoRequest = new SnsUserInfoRequest()
|
||||
{
|
||||
AccessToken = response.AccessToken,
|
||||
OpenId = response.OpenId
|
||||
};
|
||||
|
||||
var userInfoResponse = await client.ExecuteSnsUserInfoAsync(userInfoRequest, HttpContext.RequestAborted);
|
||||
if (userInfoResponse.ErrorCode == (int)WeChatReturnCode.ReturnCode.请求成功)
|
||||
{
|
||||
var weChatUserInfo = await _weChatUserInfoServices.QueryByClauseAsync(p => p.openid == response.OpenId);
|
||||
if (weChatUserInfo == null)
|
||||
{
|
||||
weChatUserInfo = new CoreCmsUserWeChatInfo()
|
||||
{
|
||||
createTime = DateTime.Now,
|
||||
type = (int)GlobalEnumVars.UserAccountTypes.微信公众号,
|
||||
//city = userInfoResponse.City,
|
||||
//country = userInfoResponse.Country,
|
||||
//province = userInfoResponse.Province,
|
||||
nickName = userInfoResponse.Nickname,
|
||||
//gender = userInfoResponse.Sex,
|
||||
avatar = userInfoResponse.HeadImageUrl,
|
||||
unionId = userInfoResponse.UnionId,
|
||||
openid = userInfoResponse.OpenId,
|
||||
gender = 1,
|
||||
//isSubscribe = userInfoResponse.PrivilegeList,
|
||||
|
||||
};
|
||||
var id = await _weChatUserInfoServices.InsertAsync(weChatUserInfo);
|
||||
if (id > 0)
|
||||
{
|
||||
await _weChatUserInfoServices.UpdateAsync(
|
||||
p => new CoreCmsUserWeChatInfo()
|
||||
{
|
||||
userId = id
|
||||
}, p => p.id == id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weChatUserInfo.nickName != userInfoResponse.Nickname || weChatUserInfo.avatar != userInfoResponse.HeadImageUrl || weChatUserInfo.unionId != userInfoResponse.UnionId)
|
||||
{
|
||||
weChatUserInfo.nickName = userInfoResponse.Nickname;
|
||||
weChatUserInfo.avatar = userInfoResponse.HeadImageUrl;
|
||||
weChatUserInfo.unionId = userInfoResponse.UnionId;
|
||||
await _weChatUserInfoServices.UpdateAsync(weChatUserInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Content("错误:" + response.ErrorMessage);
|
||||
}
|
||||
|
||||
var option = new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddSeconds(1440)
|
||||
};
|
||||
|
||||
Response.Cookies.Append(GlobalConstVars.CookieOpenId, userInfoResponse.OpenId, option);
|
||||
|
||||
if (!string.IsNullOrEmpty(bkUrl))
|
||||
{
|
||||
var outBase64String = Convert.FromBase64String(bkUrl);
|
||||
var orgStr = Encoding.Default.GetString(outBase64String);
|
||||
|
||||
return Redirect(orgStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Redirect(AppSettingsConstVars.AppConfigAppH5Url);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Content(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,18 @@
|
||||
using CoreCms.Net.Caching.AccressToken;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CoreCms.Net.Caching.AccressToken;
|
||||
using CoreCms.Net.Model;
|
||||
using CoreCms.Net.WeChat.Service.Enums;
|
||||
using CoreCms.Net.WeChat.Service.HttpClients;
|
||||
using CoreCms.Net.WeChat.Service.Options;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api;
|
||||
using SKIT.FlurlHttpClient.Wechat.Api.Models;
|
||||
|
||||
namespace CoreCms.Net.Web.WebApi.Controllers
|
||||
{
|
||||
@@ -14,14 +23,17 @@ namespace CoreCms.Net.Web.WebApi.Controllers
|
||||
[ApiController]
|
||||
public class WeChatOffiaccountController : Controller
|
||||
{
|
||||
private readonly IWeChatApiHttpClientFactory _weChatApiHttpClientFactory;
|
||||
private readonly WeChatOptions _weChatOptions;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="weChatOptions"></param>
|
||||
public WeChatOffiaccountController(IOptions<WeChatOptions> weChatOptions)
|
||||
/// <param name="weChatApiHttpClientFactory"></param>
|
||||
public WeChatOffiaccountController(IOptions<WeChatOptions> weChatOptions, IWeChatApiHttpClientFactory weChatApiHttpClientFactory)
|
||||
{
|
||||
_weChatApiHttpClientFactory = weChatApiHttpClientFactory;
|
||||
_weChatOptions = weChatOptions.Value;
|
||||
}
|
||||
|
||||
@@ -54,6 +66,93 @@ namespace CoreCms.Net.Web.WebApi.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用发送模板消息方法
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ActionResult> Send(TmpMsgModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = _weChatApiHttpClientFactory.CreateWeXinClient();
|
||||
var accessToken = WeChatCacheAccessTokenHelper.GetWeChatAccessToken();
|
||||
var request = new CgibinMessageTemplateSendRequest
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
ToUserOpenId = model.OpenId,
|
||||
TemplateId = model.TemplateId,
|
||||
MiniProgram = null,
|
||||
Data = new ConcurrentDictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(model.Url))
|
||||
{
|
||||
request.Url = model.Url;
|
||||
}
|
||||
|
||||
const string color = "#CCC";
|
||||
|
||||
if (!string.IsNullOrEmpty(model.First))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("first", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.First }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword1))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword1", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword1 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword2))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword2", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword2 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword3))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword3", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword3 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword4))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword4", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword4 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword5))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword5", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword5 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword6))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword6", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword6 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword7))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword7", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword7 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword8))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword8", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword8 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword9))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword9", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword9 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Keyword10))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("keyword10", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Keyword10 }));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Remark))
|
||||
{
|
||||
request.Data.Add(new KeyValuePair<string, CgibinMessageTemplateSendRequest.Types.DataItem>("remark", new CgibinMessageTemplateSendRequest.Types.DataItem() { Color = color, Value = model.Remark }));
|
||||
}
|
||||
|
||||
var response = await client.ExecuteCgibinMessageTemplateSendAsync(request, HttpContext.RequestAborted);
|
||||
return response.ErrorCode != (int)WeChatReturnCode.ReturnCode.请求成功
|
||||
? Json(new { ResultCode = "1", Msg = "错误:" + response.ErrorMessage })
|
||||
: Json(new { ResultCode = "0", Msg = "已发送成功", Data = response.MessageId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { ResultCode = "1", Msg = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user