我是靠谱客的博主 魁梧大山,最近开发中收集的这篇文章主要介绍短信通道模板开发,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

  1. 制作新的通道模板

该主题是针对SendGateway模块,如果需要接入新的第三方通道,则需要开发一套相应的模板。并注册到系统中,才能正常使用。Hx.SmsPlatform.Common.dll该程序集文件中,包含了开始模板时所需的相关类型与接口。

本软件是基于.net 4.5 开发,所以开发模板也需要基于.net 4.5。

注:详细代码可以参考开发模板示例文件夹下的

Hx.SmsPlatform.GatewaySms.MyTemplate通道模板

  1. 开发模板步骤
  1. 打开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_templateinfotemplateid字段,唯一键),模板名称,模板类型,短信或彩信。如下图。

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 相关问题

 

 

  1. 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,系统内部错误

最后

以上就是魁梧大山为你收集整理的短信通道模板开发的全部内容,希望文章能够帮你解决短信通道模板开发所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部