概述
- 制作新的通道模板
该主题是针对SendGateway模块,如果需要接入新的第三方通道,则需要开发一套相应的模板。并注册到系统中,才能正常使用。Hx.SmsPlatform.Common.dll该程序集文件中,包含了开始模板时所需的相关类型与接口。
本软件是基于.net 4.5 开发,所以开发模板也需要基于.net 4.5。
注:详细代码可以参考开发模板示例文件夹下的
Hx.SmsPlatform.GatewaySms.MyTemplate通道模板
- 开发模板步骤
- 打开vs2019,创建类库项目,并输入项目名称。(选择.Net Framework4.5框架,语言:C#)
2.添加对Hx.SmsPlatform.Common.dll程序集的引用
3.将Class1文件更名为SendSms。
4.再添加一个Class 命名为ProptryObject并继承Http2Property(该类在Commom中定义)。
并添加运行时所需的每个属性,
其中,
DefaultValue标记,表示该属性的默认值。
DisplayName标记,表示该属性的中文名称。
Description标记,用于该属性的说明描述。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YY.SmsPlatform.Common.Gateway;
namespace Hx.SmsPlatform.GatewaySms.MyTemplate
{
/// <summary>
/// 通道模板属性
/// </summary>
public class ProptryObject: Http2Property
{
[DefaultValue("http://IP:PORT/sms.aspx?action=send")]
public override string Url { get; set; }
[DisplayName("企业id")]
public string userid { get; set; }
[DisplayName("用户账号")]
public string account { get; set; }
[DisplayName("用户密码")]
public string password { get; set; }
[DisplayName("状态报告地址")]
[DefaultValue("http://IP:PORT/statusApi.aspx?action=query")]
public string statuUrl { get; set; }
[DisplayName("余额查询地址")]
[DefaultValue("http://IP:PORT/sms.aspx?action=overage")]
public string overageUrl { get; set; }
[DisplayName("上行地址")]
public string callUrl { get; set; }
[DisplayName("去扩展")]
[Description("下发时是否去掉扩展,选中去除扩展,不选,表示下发时,默认带扩展")]
public bool isExt { get; set; }
}
}
5.修改SendSms类,继承自Http2SendSms<>,泛型参数中传入刚才定义的ProptryObject,给SendSms类加上ChannelTemplate标记(指明该类是一个模板类),ChannelTemplate有三个参数分别表示,模板编号(对应数据库channel_templateinfo表templateid字段,唯一键),模板名称,模板类型,短信或彩信。如下图。
6.重写父类SendSms的 OnInit方法,并执行父类的初始化操作,然后为事件附加相应的方法,用于余额,状态和上行的获取,如图:
/// <summary>
/// 初始化操作,为事件附加方法(余额,状态,上行获取方法)
/// </summary>
protected override void OnInit()
{
base.OnInit(); //执行父类的初始化操作
if (string.IsNullOrWhiteSpace(this.Property.overageUrl) == false)///余额查询
{
this.Elapsed += new Action(Overage_Elapsed);
}
if (string.IsNullOrWhiteSpace(this.Property.statuUrl) == false)///状态查询(主动获取)
{
this.Elapsed += new Action(ReportTimer_Elapsed);
}
if (string.IsNullOrWhiteSpace(this.Property.callUrl) == false)///上行(主动获取)
{
this.Elapsed += new Action(moTimer_Elapsed);
}
}
7. 重写父类Http2SendSms<>的OnSend方法,在此方法中,编写远程调用逻辑,并获取返回值,进行解析,生成SendResult类返回。
参数sms提供了要发送的各种数据信息
sms.Msisdns表示手机号码的集合。可以使用Join方法将手机号拼成一个字符串。
sms.Content()表示要发送的信息的内容。
sms.ExtNumber表示下发时的扩展号码。
SendResult类表示发送的结果可由Result方法创建。
/// <summary>
/// 发送短信,返回发送结果
/// </summary>
/// <param name="sms"></param>
/// <returns></returns>
protected override SendResult OnSend(ChannelMsgObject sms)
{
string param = "&userid=" + this.Property.userid + "&account=" + this.Property.account + "&password=" + this.Property.password + "&mobile=" + string.Join(",", sms.Msisdns) + "&content=" + System.Web.HttpUtility.UrlEncode(sms.SmsContents.Concat()) + "&sendTime=";
if (this.Property.isExt == false)
{
param += "&extno=" + (this.Property.SubString == 0 ? sms.ExtNumber : sms.ExtNumber.Left(this.Property.SubString)) + "";
}
var str = HttpMethods.HttpPost(this.Property.Url, param, Encoding.GetEncoding(this.Property.Encoding));
this.LogInfo("OnSend,Result=" + str + "rn");
var doc = new System.Xml.XmlDocument();
doc.LoadXml(str);
string status = doc.SelectSingleNode("/returnsms/returnstatus").InnerText;
int mtStatus;
if (status == "Success")
{
mtStatus = 1;
}
else
{
mtStatus = -1;
}
string msgid = doc.SelectSingleNode("/returnsms/taskID").InnerText;
string gatawayCode = doc.SelectSingleNode("/returnsms/returnstatus").InnerText;
string description = doc.SelectSingleNode("/returnsms/message").InnerText;
//用于返回的信息
SendResult r = new SendResult(mtStatus, msgid, gatawayCode, description);
return r;
}
其中,mtStatus表示发送的结果,1为成功,其它为失败(通常是-1)。
messageId表示发送的唯一标识,在匹配状态报告时会用到。当下发成功后,通道会返回一个对应的任务批次,messageId取此值,如果下发失败,messageId为空。
resultCode表示通道返回的代码,表示下发请求的状态,通常取任务状态吗
description发送结果的文本描述,此值会体现到界面中,由其在失败时,此值可以记录失败的原因。如果返回值中未包含此值,description可以为空,如果对应的状态吗有错误描述,可以将
8.余额获取,为事件Elapsed(30秒执行一次)附加方法Balance_Elapsed,执行余额查询的相关逻辑操作,使用SetBalance方法设置当前通道余额,如图
/// <summary>
/// 余额查询
/// </summary>
private void Overage_Elapsed()
{
try
{
var resultXml = this.Post(this.Property.overageUrl, new
{
this.Property.userid,
this.Property.account,
this.Property.password
});
this.LogInfo("余额返回:" + resultXml);
var doc = new System.Xml.XmlDocument();
doc.LoadXml(resultXml);
var status = doc.SelectSingleNode("returnsms/returnstatus").InnerText;
if (status == "Sucess")
{
var overage = doc.SelectSingleNode("returnsms/overage").InnerText;
this.SetBalance(int.Parse(overage));
}
}
catch (Exception ex)
{
this.LogError("余额获取,ChannelId={0},ex={1}", ChannelId, ex);
}
}
9.状态报告返回一个ReportObject对象集合,状态报告为主动获取时,需要为事件Elapsed附加一个方法,Report_Elapsed,在方法里进行状态的获取,并将获取到的状态使用NotifyReceiveReport通知到父类,进行下一步的处理,如图:
/// <summary>
/// 状态获取
/// </summary>
private void ReportTimer_Elapsed()
{
try
{
string str = this.Post(this.Property.statuUrl, new
{
this.Property.userid,
this.Property.account,
this.Property.password
});
this.LogInfo("Report={0}", str);
if (string.IsNullOrWhiteSpace(str))
{
return;
}
str = str.CleanInvalidXmlChars();
var doc = new System.Xml.XmlDocument();
doc.LoadXml(str); //解析str
List<ReportObject> rlist = new List<ReportObject>(); //用于接收返回的状态
if (doc.DocumentElement.HasChildNodes)
{
string firstChildName = doc.DocumentElement.FirstChild.Name;
if (firstChildName == "statusbox")
{
System.Xml.XmlNodeList nodelist = doc.SelectNodes("/returnsms/statusbox");
foreach (System.Xml.XmlNode item in nodelist)
{
string mobile = item.SelectSingleNode("mobile").InnerText;
string taskid = item.SelectSingleNode("taskid").InnerText;
string status = item.SelectSingleNode("status").InnerText;
string receivetime = item.SelectSingleNode("receivetime").InnerText;
string errorcode = item.SelectSingleNode("errorcode").InnerText;
string extno = item.SelectSingleNode("extno").InnerText;
if (errorcode.Length > 80)
{
errorcode = errorcode.Substring(0, 75);
}
var rStatus = status == "10" ? "DELIVRD" : errorcode;
var a = NewReportObject(mobile, taskid, rStatus); //使用NewReportObject方法创建ReportObject对象
a.ReceiveTime = DateTime.Now;
rlist.Add(a);
}
this.NotifyReceiveReport(rlist); //使用NotifyReceiveReport方法通知父类接受状态
}
}
}
catch (Exception ex)
{
this.LogError("状态获取,ChannelId={0},ex={1}", ChannelId, ex);
}
}
状态报告为推送时,需要继承IHttpReceiveReport接口,显示实现OnPost方法,在OnPost方法中进行相应的逻辑处理,并向上级返回一个ReportObject对象,具体参见Hx.SmsPlatform.GatewaySms.MyTemplate通道模板示例,如图:
/// <summary>
/// 接收推送过来的状态报告
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
IEnumerable<ReportObject> IHttpReceiveReport.OnPost(HttpContextBase context)
{
var request = context.Request;
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string result = reader.ReadToEnd();
this.LogInfo("Report,result={0}", result);
if (string.IsNullOrWhiteSpace(result))
{
yield break;
}
Report[] reports = JsonHelper.JsonDeserialize<Report[]>(result);
foreach (Report report in reports)
{
string reportZT = report.status;
Trace.TraceInformation("Report,result:phone={0},reportTime={1},status={2}", report.mobile, report.receivetime, reportZT);
yield return new ReportObject()
{
MessageId = report.taskid,
Msisdn = MessageUtil.CleanMsisdn(report.mobile),
ReportStatus = reportZT,//DELIVRD
ReceiveTime = string.IsNullOrWhiteSpace(report.receivetime) ? DateTime.Now : Convert.ToDateTime(report.receivetime)
};
}
context.Response.ContentType = "application/json;charset=utf-8";
context.Response.Write("0");
}
10.上行为主动获取时,需要为事件Elapsed附加一个方法,Mo_Elapsed,在方法里进行上行的相关逻辑处理,并将获取到的上行使用NotifyReceiveMo通知到父类,进行下一步的处理,如图:
/// <summary>
/// 上行获取
/// </summary>
private void moTimer_Elapsed()
{
try
{
var resultXml = this.Post(this.Property.callUrl, new
{
this.Property.userid,
this.Property.account,
this.Property.password
});
this.LogInfo("上行返回:" + resultXml);
if (string.IsNullOrWhiteSpace(resultXml))
{
return;
}
resultXml = resultXml.CleanInvalidXmlChars();
var doc = new System.Xml.XmlDocument();
doc.LoadXml(resultXml);
var list = doc.SelectNodes("/returnsms/callbox");
foreach (System.Xml.XmlNode item in list)
{
string mobile = item.SelectSingleNode("mobile").InnerText;
string taskid = item.SelectSingleNode("taskid").InnerText;
string content = item.SelectSingleNode("content").InnerText;
string receivetime = item.SelectSingleNode("receivetime").InnerText;
string extno = item.SelectSingleNode("extno").InnerText;
var extNumber = base.ExtractMedian(extno);
this.NotifyReceiveMo(mobile, content, extNumber, extno); //使用NotifyReceiveMo方法通知父类收到上行
}
}
catch (Exception ex)
{
this.LogError("上行获取,ChannelId={0},ex={1}", ChannelId, ex);
}
}
上行为推送时,需要继承IHttpReceiveReport接口,显示实现OnPost方法,在OnPost方法中进行相应的逻辑处理,并向上级返回一个MoObject对象,具体参见Hx.SmsPlatform.GatewaySms.MyTemplate通道模板示例,如图:
/// <summary>
/// 接收推送过来的MO
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
IEnumerable<MoObject> IHttpReceiveMultiMo.OnPost(HttpContextBase context)
{
var request = context.Request;
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string result = reader.ReadToEnd();
this.LogInfo("MO,result={0}", result);
if (string.IsNullOrWhiteSpace(result))
{
yield break;
}
Mo mo = JsonHelper.JsonDeserialize<Mo>(result);
Trace.TraceInformation("MO,result:mobile={0},Sender={1},Message={2},RecvTime={3}", mo.mobile, mo.extend, mo.content, mo.reply_time);
yield return new MoObject()
{
Msisdn = mo.mobile,
RawNumber = mo.extend,
ExtNumber = mo.extend,
Text = mo.content,
ReceiveTime = string.IsNullOrWhiteSpace(mo.reply_time) ? DateTime.Now : Convert.ToDateTime(mo.reply_time),
MsgType = MsgType.Sms
};
context.Response.ContentType = "application/json;charset=utf-8";
context.Response.Write("0");
}
}
public class Report
{
public string taskid { get; set; }
public string mobile { get; set; }
public string status { get; set; }
public string receivetime { get; set; }
}
public class Mo
{
public string moid { get; set; }
public string mobile { get; set; }
public string content { get; set; }
public string sign { get; set; }
public string extend { get; set; }
public string reply_time { get; set; }
}
11.将生成的程序集文件放到Hx.SmsPlatform.SendGateway文件夹下,如果状态或者上行有需要推送的,将程序集文件在Hx.SmsPlatform.ReceiveMoReport网站的bin目录下,放一份该模板的程序集文件。然后使用Hx.SmsPlatform.WindowsForms程序的TemPlate窗口,将通道模板注册到程序中,
,然后在通道界面,选择新增通道,选择新加入的通道模板创建通道,选择刚才的通道模板创建通道,完成。
2.类库参考
相关的类型都在Hx.SmsPlatform.Common.Gateway名称空间下。
ISendSms接口
该接口是任何模板的最基础类型,只要实现该接口便可以作为模板类存在。该接口包含以下成员:
方法:
OnInit是在第一次加载时被调用,可以在此方法内执行一些初始化操作。方法的三个参数分别是
通道对象,模板属性数组和数据源对象。
Send方法是在发送短信时被调用。这是同步发送方式。返回值为发送结果。
SendAsync方法是使用异步的方法进行发送,并不返回值,而是应该在在发送完成时,触发SendCompleted事件。
属性:
IsAsync ,指定是否使用异步方式发送,如果使用异步方式发送,重写此属性返回true,并重写SendAsync方法,实现相应的逻辑处理。此值为true时,会调用SendAsync方法,OnSend只需重写,不需要实现具体的逻辑代码。不会调用OnSend方法。
注:使用SendAsync异步方法发送时,才需要设置此值,否则会引发意外的情况
IsSplit ,指定发送短信时,是否将长短信拆分为单条发送,true表示将长短信拆分为单条发送。
注:使用SendAsync异步方法发送时,才需要设置此值,否则会引发意外的情况
事件:
SendCompleted当使用异步方法时,在发送结束时,触发该事件。在一个SendAsync方法中,可以多次触发该事件。
ReceiveMo在接收到上行时触发该事件。
ReceiveReport在接收到状态报告时触发该事件。
Http2SendSms<TProperty>
该类是ISendSms一个具体实现,是Http通迅方式的基类。该类包含以下成员:
方法:
Get(NameValueCollection),以get方式向Property.Url发送请求。参数为键值集合。
Get(string),以get方式向Property.Url发送请求。参数为字符串。
Get<T>(T),以get方式向Property.Url发送请求。参数为对象,以对象属性作为键值。
Get<T>(string, T),以get方式向指定的url发送请求。参数为对象,以对象属性作为键值。
Post (NameValueCollection),以post方式向Property.Url发送请求。参数为键值集合。
Post (string),以post方式向Property.Url发送请求。参数为字符串。
Post<T>(T),以post方式向Property.Url发送请求。参数为对象,以对象属性作为键值。
Post<T>(string, T),以post方式向指定的url发送请求。参数为对象,以对象属性作为键值。
UrlEncode对指定的内容进行Url编码,并返回编码后的字符串。
属性:
Encoding表示当前的编码方式。由Property. Encoding生成。
事件:
Elapsed间隔性(30秒)引发该事件。可以用于轮询性操作,如查询余额、状态和上行等。
Http2Property
该类是作为Http2SendSms<TProperty>泛型参数的基类存在。并提供了基础属性。
属性:
Url,信息提交的接口地址。
Encoding,所使用的编码方法,默认为utf8。
Timeout,超时时间,以毫秒为单位,默认为30*1000即30秒。
IHttpReceiveReport
该接口用于在需要通道方推送状态时。SendSms类继承该接口,并实现OnPost方法。
注:OnPost中,请不要引用SendSms的对象成员,因为成员并未初始化,可能会导致意外的行为,在发布时,应该在Hx.SmsPlatform.ReceiveMoReport的bin目录下,放一份该模板的程序集文件。
IHttpReceiveMo
该接口用于在需要通道方推送上行时。SendSms类继承该接口,并实现OnPost方法。
注:OnPost中,请不要引用SendSms的对象成员,因为成员并未初始化,可能会导致意外的行为,在发布时,应该在Hx.SmsPlatform.ReceiveMoReport的bin目录下,放一份该模板的程序集文件。
IhttpReceiveMultiMo
该接口用于在需要通道方推送上行时。SendSms类继承该接口,并实现OnPost方法。
注:OnPost中,请不要引用SendSms的对象成员,因为成员并未初始化,可能会导致意外的行为,在发布时,应该在Hx.SmsPlatform.ReceiveMoReport的bin目录下,放一份该模板的程序集文件。
2.cmpp 相关问题
- Cmpp开账号规则
Cmpp客户对接除一下参数外,其他参数不填,cmpp客户对应的日志文件在HxHx.SmsPlatform.TcpServer20文件夹下,对应的用户日志在HxTcpServer20userid 文件夹下
服务器地址:Host 139.129.215.85(服务器ip)
服务器端口:Port 7890
企业代码:SPID 账号(6位数)
用户名:UserName 账号(6位数)
密码 密码
接入代码:Src_Id 扩展号
2.Cmpp返回值描述
cmpp2.0提交说明
链接错误码
0,正常
1,消息结构错误
2,账户信息错误
3,密码错误
19,ip绑定错误
15,超过最大链接数
提交错误码
0,正确
1,消息结构错误
2,命令字错
3,消息序号重复
4,消息长度错误
5,自费代码错误
6,超过最大信息长度,消息内容错误
7,业务代码错误
8,流量控制错误
9,其他错误
10. 接入号码错误
11. 链接数超过指定次数
13,接收号码错误
101,长短信,Pk_total值错误
102,长短信Pk_number小宇1,或Pk_number大于Pk_total
52,未登录
255. 系统错误
平台状态错误码
MY:1001,参数错误
MY:2001,内容为空
MY:1103,手机号码为空
MY:1114,接入号码错误
MY:1108,错误的手机号码
MY:2105,信息内容长度错误
MY:1112,没有配置产品
MY:2113,没有签名
MY:2114,签名错误
MY:2107,敏感词
MY:1111,额度不足
MY:9999,系统内部错误
最后
以上就是魁梧大山为你收集整理的短信通道模板开发的全部内容,希望文章能够帮你解决短信通道模板开发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复