//Class WechatOfferExcutor
此类为微信触发类,属于上层调用类,其中有用到用静态变量缓存offer信息,Task异步执行发送方法等
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Velites.Utility.SharedLibrary;
using Newtonsoft.Json;
using System.Configuration;
namespace xxx.Peripheral.Wechat
{
[ParametrType(typeof(WechatOfferParameter))]
public class WechatOfferExcutor : MemberOfferExecutorBase<WechatOfferParameter>
{
static KeyValuePair<long, WechatOfferParameter> offerInfo = new KeyValuePair<long, WechatOfferParameter>();
static WechatBatchProcess proc = new WechatBatchProcess(Send);
protected override IOfferExecutionResult ExecuteWithIDataAndMember(IData data, IMember member, WechatOfferParameter offer)
{
bool isValid = AssertHits(data, member, offer);
LogStub.Log(new LogEntry("txt - AssertHits isValid", LogStub.LOG_LEVEL_ERROR));
if (isValid && !string.IsNullOrWhiteSpace(offer.Content))
{
string memCode = member.GetProperty(ThreeMMemberEntitySchema.ThreeMMember.CODE).ToString();
string toWechat = member.GetProperty(ThreeMMemberEntitySchema.ThreeMMember.WECHAT_OPENID).ToString();
List<ImgItem> itemList = new List<ImgItem>()
{
new ImgItem() {
title = offer.Subject,
content = offer.Content
}
};
LogEntry entry = new LogEntry(string.Format("txt - 微信发送开始, memCode = {0}, wechatOpenID = {1}, offerID = {2}", memCode, toWechat, offer.OfferID), LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
proc.AddOpenIDAndSendWechat(offer.OfferID, toWechat, itemList, offer.ExecuteTime);
//EmailServiceApiConfigurationHub.Instance.EmailProvider.Provide().Send(wechat);
LogEntry endEntry = new LogEntry("txt - 微信发送结束", LogStub.LOG_LEVEL_ERROR);
LogStub.Log(endEntry);
}
return null;
}
private static void Send(List<string> openIDs, List<ImgItem> imgDesc)
{
//注意,此处必须为异步调用发送方法,否则可能导致发送超时,以至于发送失败
Task.Run(() => WechatUtility.SendText(openIDs, imgDesc));
offerInfo = new KeyValuePair<long, WechatOfferParameter>();
}
public bool AssertHits(IData data, IMember member, WechatOfferParameter offer)
{
bool ret = true;
if (data == null || member == null || offer == null || member.GetProperty(ThreeMMemberEntitySchema.ThreeMMember.WECHAT_OPENID) == null
|| string.IsNullOrWhiteSpace(member.GetProperty(ThreeMMemberEntitySchema.ThreeMMember.WECHAT_OPENID).ToString()))
{
ret = false;
return ret;
}
return ret;
}
public override WechatOfferParameter ParseToParameter(JObject jOffer, IOffer offer)
{
if (offerInfo.Key != offer.ID)
{
WechatOfferParameter wechatOffer = base.ParseToParameter(jOffer, offer);
wechatOffer.Category = offer.CategoryKey;
var offerParameterToken = JToken.Parse(offer.Parameter);
if (offerParameterToken["content"] != null)
{
wechatOffer.Content = offerParameterToken["content"].ToString();
wechatOffer.Subject = offer.Name;
wechatOffer.OfferID = offer.ID;
//此处给发送时间赋值
//如果是市场活动,没有执行时间,那么可以设置为当前时间的5分钟之后
//测试时可以设置为10秒
//wechatOffer.ExecuteTime = DateTime.Now.AddSeconds(10);
wechatOffer.ExecuteTime = DateTime.Now.AddMinutes(5);
var interval = ConfigurationManager.AppSettings["ActivityInterval"];
if (interval != null)
{
wechatOffer.ExecuteTime = DateTime.Now.AddMinutes(Int32.Parse(interval.ToString()));
}
if (jOffer != null)
{
IDictionary<string, JToken> data = JsonConvert.DeserializeObject<JObject>(jOffer.ToString());
var item = data.Where(p => p.Key.ToLower() == "executetime");
if (item != null && item.Any())
{
wechatOffer.ExecuteTime = Convert.ToDateTime(item.FirstOrDefault().Value);
}
}
offerInfo = new KeyValuePair<long, WechatOfferParameter>(offer.ID, wechatOffer);
return wechatOffer;
}
}
return offerInfo.Value;
}
}
}
//Class WechatBatchProcess
此类是发送微信的中间处理类,包括了Timer间隔触发等,并发处理Lock机制等,其他的项目可以借鉴此类。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace ThreeM.Peripheral.Wechat
{
public class WechatBatchProcess
{
List<SendInfo> SendInfoList = new List<SendInfo>();
Action<List<string>, List<ImgItem>> sendAct { set; get; }
Timer timer;
public WechatBatchProcess(Action<List<string>, List<ImgItem>> act)
{
timer = new Timer();
sendAct = act;
//正式的可以改10分钟,测试时可以为30000即30秒
timer.Interval = 600000;
var interval = ConfigurationManager.AppSettings["TimerInterval"];
if (interval != null)
{
timer.Interval = Int32.Parse(interval);
}
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(Timers_Timer_Elapsed);
}
void Timers_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (this)
{
var itemList = SendInfoList.Where(p => p.SendTime <= DateTime.Now).ToList();
SendInfoList = SendInfoList.Where(p => p.SendTime > DateTime.Now).ToList();
foreach (var item in itemList)
{
Send(item);
}
if (SendInfoList.Any())
{
timer.Start();
}
}
}
public void AddOpenIDAndSendWechat(long OfferID, string OpenID, List<ImgItem> ImgDesc, DateTime? SendTime)
{
if (!string.IsNullOrWhiteSpace(OpenID) && ImgDesc != null && ImgDesc.Any())
{
lock (this)
{
var item = SendInfoList.FirstOrDefault(p => p.OfferID == OfferID);
if (item == null)
{
item = new SendInfo()
{
OfferID = OfferID,
OpenidList = new List<string>(),
ImgDesc = ImgDesc,
};
SendInfoList.Add(item);
}
item.SendTime = SendTime;
item.OpenidList.Add(OpenID);
}
timer.Start();
}
}
private void Send(SendInfo item)
{
if (item.OpenidList.Any())
{
sendAct(item.OpenidList, item.ImgDesc);
}
}
}
public class SendInfo
{
public List<string> OpenidList { get; set; }
public List<ImgItem> ImgDesc { get; set; }
public long OfferID { get; set; }
public DateTime? SendTime { get; set; }
}
}
//Class WechatUtility
此类为基础工具类,包含了发送微信时需要的HttpPost请求等方法,
包括文本请求和多张图片请求等,是一个非常有用的工具类
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Configuration;
using System.Collections.Specialized;
using Velites.Utility.SharedLibrary;
namespace xxx.Peripheral.Wechat
{
public class WechatUtility
{
public static string SendText(List<string> openIDList, string content)
{
string url = ConfigurationManager.AppSettings["wechatImgUrl"];
string result = string.Empty;
if (openIDList != null && openIDList.Any() && !string.IsNullOrWhiteSpace(content))
{
string param = string.Format("open_ids={0}&text={1}", string.Join(",",openIDList), content);
result = HttpPostData(url, param);
}
return result;
}
public static string SendText(List<string> openIDList, List<ImgItem> imgDesc)
{
string url = ConfigurationManager.AppSettings["wechatImgUrl"];
string result = string.Empty;
var imgInfo = imgDesc.FirstOrDefault();
if (openIDList != null && openIDList.Any() && !string.IsNullOrWhiteSpace(imgInfo.content))
{
//微信群发要求至少2个账号才能发送,如果只有一个时,添加一个相同的微信号
if (openIDList.Count() == 1)
{
openIDList.Add(openIDList.FirstOrDefault());
}
string param = string.Format("open_ids={0}&text={1}", string.Join(",", openIDList), imgInfo.content);
result = HttpPostData(url, param);
}
return result;
}
public static string SendImgAndText(List<string> openIDList, string title, string content, string filePath)
{
string url = ConfigurationManager.AppSettings["wechatImgUrl"];
string result = string.Empty;
if (openIDList != null && openIDList.Any() && !string.IsNullOrWhiteSpace(content))
{
NameValueCollection col = new NameValueCollection();
//col.Add("media", filePath);
col.Add("title", title);
col.Add("content", content);
col.Add("open_ids", string.Join(",", openIDList));
result = HttpPostData(url, 60000, "media1", filePath, col);
}
return result;
}
public static string SendImgAndText(List<string> openIDList, List<ImgItem> imgDesc)
{
string url = ConfigurationManager.AppSettings["wechatImgUrl"];
string result = string.Empty;
if (openIDList != null && openIDList.Any() && imgDesc != null)
{
Dictionary<string, string> imgDic = new Dictionary<string, string>();
foreach (var item in imgDesc)
{
imgDic.Add(item.name, item.path);
}
NameValueCollection col = new NameValueCollection();
col.Add("imgDesc", Newtonsoft.Json.JsonConvert.SerializeObject(imgDesc));
col.Add("open_ids", string.Join(",", openIDList));
result = HttpPostData(url, 60000, imgDic, col);
}
return result;
}
public static string HttpPostData(string url, string param)
{
LogEntry entry = new LogEntry("发送微信文本开始-HttpPostData -- " + param, LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
var result = string.Empty;
//注意提交的编码 这边是需要改变的 这边默认的是Default:系统当前编码
byte[] postData = Encoding.UTF8.GetBytes(param);
// 设置提交的相关参数
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
Encoding myEncoding = Encoding.UTF8;
request.Method = "POST";
request.KeepAlive = false;
request.AllowAutoRedirect = true;
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)";
request.ContentLength = postData.Length;
// 提交请求数据
System.IO.Stream outputStream = request.GetRequestStream();
outputStream.Write(postData, 0, postData.Length);
outputStream.Close();
HttpWebResponse response;
Stream responseStream;
StreamReader reader;
string srcString;
response = request.GetResponse() as HttpWebResponse;
responseStream = response.GetResponseStream();
reader = new System.IO.StreamReader(responseStream, Encoding.GetEncoding("UTF-8"));
srcString = reader.ReadToEnd();
result = srcString; //返回值赋值
reader.Close();
entry = new LogEntry("发送微信文本结束-HttpPostData -- " + result, LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
return result;
}
private static string HttpPostData(string url, int timeOut, string fileKeyName,
string filePath, NameValueCollection stringDict)
{
string fileKeyName2 = "media2";
string filePath2 = @"C:\Users\gaoj007\Pictures\1aliuh.jpg";
string responseContent;
var memStream = new MemoryStream();
var webRequest = (HttpWebRequest)WebRequest.Create(url);
// 边界符
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
// 边界符
var beginBoundary = Encoding.ASCII.GetBytes("--" + boundary + "\r\n");
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
// 最后的结束符
var endBoundary = Encoding.ASCII.GetBytes("--" + boundary + "--\r\n");
// 设置属性
webRequest.Method = "POST";
webRequest.Timeout = timeOut;
webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
// 写入文件
const string filePartHeader =
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n";
var header = string.Format(filePartHeader, fileKeyName, filePath);
var headerbytes = Encoding.UTF8.GetBytes(header);
memStream.Write(beginBoundary, 0, beginBoundary.Length);
memStream.Write(headerbytes, 0, headerbytes.Length);
var buffer = new byte[1024];
int bytesRead; // =0
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
memStream.Write(buffer, 0, bytesRead);
}
//第二章图片
//memStream.Write(beginBoundary, 0, beginBoundary.Length);
//var aaa = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
//memStream.Write(aaa, 0, aaa.Length);
string nxetFileFormat = "\r\n--" + boundary + "\r\n" + filePartHeader;
header = string.Format(nxetFileFormat, fileKeyName2, filePath2);
headerbytes = Encoding.UTF8.GetBytes(header);
memStream.Write(headerbytes, 0, headerbytes.Length);
fileStream = new FileStream(filePath2, FileMode.Open, FileAccess.Read);
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
memStream.Write(buffer, 0, bytesRead);
}
// 写入字符串的Key
var stringKeyHeader = "\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"{0}\"" +
"\r\n\r\n{1}\r\n";
foreach (byte[] formitembytes in from string key in stringDict.Keys
select string.Format(stringKeyHeader, key, stringDict[key])
into formitem
select Encoding.UTF8.GetBytes(formitem))
{
memStream.Write(formitembytes, 0, formitembytes.Length);
}
// 写入最后的结束边界符
memStream.Write(endBoundary, 0, endBoundary.Length);
webRequest.ContentLength = memStream.Length;
var requestStream = webRequest.GetRequestStream();
memStream.Position = 0;
var tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer, 0, tempBuffer.Length);
requestStream.Close();
var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(),
Encoding.GetEncoding("utf-8")))
{
responseContent = httpStreamReader.ReadToEnd();
}
fileStream.Close();
httpWebResponse.Close();
webRequest.Abort();
return responseContent;
}
private static string HttpPostData(string url, int timeOut, Dictionary<string, string> imgDic, NameValueCollection stringDict)
{
LogEntry entry = new LogEntry("发送微信图片开始-HttpPostData open_ids-- " + stringDict["open_ids"], LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
try
{
entry = new LogEntry("发送微信图片开始-HttpPostData imgDic-- " + string.Join(",", imgDic), LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
entry = new LogEntry("发送微信图片开始-HttpPostData imgDic-- first--" + imgDic.FirstOrDefault().Key + " -- " +
imgDic.FirstOrDefault().Value + "--last--" + imgDic.LastOrDefault().Key + " -- " + imgDic.LastOrDefault().Value + "--count--" + imgDic.Count(), LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
var firstImg = imgDic.FirstOrDefault();
string fileKeyName = firstImg.Key;
string filePath = firstImg.Value;
string responseContent;
var memStream = new MemoryStream();
var webRequest = (HttpWebRequest)WebRequest.Create(url);
// 边界符
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
// 边界符
var beginBoundary = Encoding.ASCII.GetBytes("--" + boundary + "\r\n");
//var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
// 最后的结束符
var endBoundary = Encoding.ASCII.GetBytes("--" + boundary + "--\r\n");
// 设置属性
webRequest.Method = "POST";
webRequest.Timeout = timeOut;
webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
// 写入文件
const string filePartHeader =
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n";
var header = string.Format(filePartHeader, fileKeyName, filePath);
var headerbytes = Encoding.UTF8.GetBytes(header);
memStream.Write(beginBoundary, 0, beginBoundary.Length);
memStream.Write(headerbytes, 0, headerbytes.Length);
//var buffer = new byte[1024];
//int bytesRead; // =0
//while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
//{
// memStream.Write(buffer, 0, bytesRead);
//}
WebClient wc = new WebClient();
byte[] buffer = wc.DownloadData(filePath);
memStream.Write(buffer, 0, buffer.Length);
string imgName = string.Empty;
string imgPath = string.Empty;
foreach (var img in imgDic.Where(p => p.Key != fileKeyName))
{
imgName = img.Key;
imgPath = img.Value;
string nxetFileFormat = "\r\n--" + boundary + "\r\n" + filePartHeader;
header = string.Format(nxetFileFormat, imgName, imgPath);
headerbytes = Encoding.UTF8.GetBytes(header);
memStream.Write(headerbytes, 0, headerbytes.Length);
//fileStream = new FileStream(imgPath, FileMode.Open, FileAccess.Read);
//while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
//{
// memStream.Write(buffer, 0, bytesRead);
//}
buffer = wc.DownloadData(imgPath);
memStream.Write(buffer, 0, buffer.Length);
}
// 写入字符串的Key
var stringKeyHeader = "\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"{0}\"" +
"\r\n\r\n{1}\r\n";
foreach (byte[] formitembytes in from string key in stringDict.Keys
select string.Format(stringKeyHeader, key, stringDict[key])
into formitem
select Encoding.UTF8.GetBytes(formitem))
{
memStream.Write(formitembytes, 0, formitembytes.Length);
}
// 写入最后的结束边界符
memStream.Write(endBoundary, 0, endBoundary.Length);
webRequest.ContentLength = memStream.Length;
var requestStream = webRequest.GetRequestStream();
memStream.Position = 0;
var tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer, 0, tempBuffer.Length);
requestStream.Close();
var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(),
Encoding.GetEncoding("utf-8")))
{
responseContent = httpStreamReader.ReadToEnd();
}
//fileStream.Close();
httpWebResponse.Close();
webRequest.Abort();
entry = new LogEntry("发送微信图片结束-HttpPostData -- " + responseContent, LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
return responseContent;
}
catch (Exception er)
{
entry = new LogEntry("发送微信图文发生异常 -- HttpPostData -- " + er.ToString(), LogStub.LOG_LEVEL_ERROR);
LogStub.Log(entry);
}
return null;
}
}
}