ASP.NET MVC5+EF6+EasyUI 后台管理系統(71)-微信公眾平台開發-公眾號管理


系列目錄

思維導圖

下面我們來看一個思維導圖,這樣就可以更快了解所需要的功能:

 

上一節我們利用了一個簡單的代碼例子,完成了與微信公眾號的對話(給公眾號發一條信息,並得到回復)

這一節將講解公眾號如何設置,雖然公眾號管理只是一張表,但是設計起來還是有一些技巧

1.一個企業可能底下有多個業務公眾號在同一個系統中需要處理(用戶發起的請求,是對應我們哪個公眾號)

2.多個公眾號下,后台如何取得操作比較方便(設置當前公眾號為默認操作號)

3.可以手動刷新Access_Token,因為我們隨時要保持Access_Token可用,這是調用微信接口的主要令牌(我們后面將講解定時更新,而非手動)

知識點

1.表設計

2.設置為默認公眾號

3.生成指定格式的URL資源服務器

4.更新Access_Token 

表設計

表的設計沒有太多的成分,我們根據公眾號的信息,自己建立對應的字段,下面是我已經已建立好的數據表

CREATE TABLE [dbo].[WC_OfficalAccounts](
    [Id] [varchar](50) NOT NULL,              --主鍵
    [OfficalId] [varchar](200) NULL,          --公眾號的唯一ID
    [OfficalName] [varchar](200) NOT NULL,    --公眾號名稱
    [OfficalCode] [varchar](200) NOT NULL,    --公眾號帳號
    [OfficalPhoto] [varchar](1000) NULL,      --頭像
    [OfficalKey] [varchar](500) NULL,         --EncodingAESKey
    [ApiUrl] [varchar](1000) NULL,            --我們的資源服務器
    [Token] [varchar](200) NULL,              --Token
    [AppId] [varchar](200) NULL,              --AppId
    [AppSecret] [varchar](200) NULL,          --Appsecret
    [AccessToken] [varchar](200) NULL,        --訪問Token
    [Remark] [varchar](2000) NULL,            --說明
    [Enable] [bit] NOT NULL,                  --是否啟用
    [IsDefault] [bit] NOT NULL,               --是否為當前默認操作號
    [Category] [int] NOT NULL,                --類別(媒體號,企業號,個人號,開發測試號)
    [CreateTime] [datetime] NOT NULL,         --創建時間
    [CreateBy] [varchar](50) NOT NULL,        --創建人
    [ModifyTime] [datetime] NOT NULL,         --修改時間
    [ModifyBy] [varchar](50) NULL,            --修改人
 CONSTRAINT [PK_WC_OfficalAcconts] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

每個字段我都有注釋,但是可能還不夠明白,我們來看網站對應的數據,這樣更加清楚一點

 

這5個字段是必要的,其他其實都是非必要的。我寫了那么多,只是讓后台管理能夠得知當前操作號的信息情況,所以到時創建的時候,這個是對應的填寫字段

程序設計

操作EF的增刪該查,我在這里就不做代碼演示了,已經在前面的相同功能演示過很多遍(下載尾部代碼或者自己動手做起來)

我們來看主要的功能代碼。

設置公眾號為當前的操作號

這里我以字段IsDefault來標識,哪個公眾號為系統當前的操作公眾號

第一、設置所有公眾號的IsDefault為Flase

第二、設置指定ID的公眾號的IsDefault為True

update [dbo].[WC_OfficalAccounts] set IsDefault=0
update [dbo].[WC_OfficalAccounts] set IsDefault=1 where id='XXXXXXX'
 public bool SetDefault(string id)
 {
            //更新所有為不默認0
       ExecuteSqlCommand(@"update [dbo].[WC_OfficalAccounts] set IsDefault=0");
            //設置當前為默認1
       return ExecuteSqlCommand(@"update [dbo].[WC_OfficalAccounts] set IsDefault=1 where id='"+id+"'")>0;
 }

我們之前系統並未出現,這種直接執行Sql語句的,所以我寫了ExecuteSqlCommand這個方法(還有其他幾個,可能以后會用到),擴展在倉儲中BaseRepository

  /// <summary>
        /// 執行一條SQL語句
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public int ExecuteSqlCommand(string sql)
        {
            return Context.Database.ExecuteSqlCommand(sql);
        }
        /// <summary>
        /// 異步執行一條SQL語句
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public Task<int> ExecuteSqlCommandAsync(string sql)
        {
            return Context.Database.ExecuteSqlCommandAsync(sql);
        }

        public DbRawSqlQuery<T> SqlQuery(string sql)
        {
            return db.Database.SqlQuery<T>(sql);
        }
        /// <summary>
        /// 查詢一條語句返回結果集
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public DbRawSqlQuery<T> SqlQuery(string sql,params object[] paras)
        {
            return db.Database.SqlQuery<T>(sql,paras);
        }

這里肯定有人會問,你那個可能會被注入,沒錯,實際應該用參數的方式,但是我在過濾器處理了SQL的注入。

那么在過濾器如果處理注入?這是擴展出另一個問題了,如果感興趣,展開下面代碼(莫非是對傳入的參數進行格式處理)

 var actionParameters = filterContext.ActionDescriptor.GetParameters();
            foreach (var p in actionParameters)
            {
                if (p.ParameterType == typeof(string))
                {
                    if (filterContext.ActionParameters[p.ParameterName] != null)
                    {
                        filterContext.ActionParameters[p.ParameterName] = ResultHelper.Formatstr(filterContext.ActionParameters[p.ParameterName].ToString());
                    }
                }
            }
OnActionExecuting
 public static string Formatstr(string html)
        {
            System.Text.RegularExpressions.Regex regex1 = new System.Text.RegularExpressions.Regex(@"<script[\s\S]+</script *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex2 = new System.Text.RegularExpressions.Regex(@" href *= *[\s\S]*script *:", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex3 = new System.Text.RegularExpressions.Regex(@" on[\s\S]*=", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex4 = new System.Text.RegularExpressions.Regex(@"<iframe[\s\S]+</iframe *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex5 = new System.Text.RegularExpressions.Regex(@"<frameset[\s\S]+</frameset *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex10 = new System.Text.RegularExpressions.Regex(@"select", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex11 = new System.Text.RegularExpressions.Regex(@"update", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            System.Text.RegularExpressions.Regex regex12 = new System.Text.RegularExpressions.Regex(@"delete", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            html = regex1.Replace(html, ""); //過濾<script></script>標記
            html = regex2.Replace(html, ""); //過濾href=javascript: (<A>) 屬性
            html = regex3.Replace(html, " _disibledevent="); //過濾其它控件的on...事件
            html = regex4.Replace(html, ""); //過濾iframe
            html = regex10.Replace(html, "s_elect");
            html = regex11.Replace(html, "u_pudate");
            html = regex12.Replace(html, "d_elete");
            html = html.Replace("'", "");
            html = html.Replace("&nbsp;", " ");
            return html;
        }
Formatstr

獲得當前公眾號

業務層

 public WC_OfficalAccountsModel GetCurrentAccount()
        {
            WC_OfficalAccounts entity =  m_Rep.GetCurrentAccount();
            if (entity == null)
            {
                return new WC_OfficalAccountsModel();
            }
            WC_OfficalAccountsModel model = new WC_OfficalAccountsModel();
            model.Id = entity.Id;
            model.OfficalName = entity.OfficalName;
            model.OfficalCode = entity.OfficalCode;
            model.OfficalPhoto = entity.OfficalPhoto;
            model.ApiUrl = entity.ApiUrl;
            model.Token = entity.Token;
            model.AppId = entity.AppId;
            model.AppSecret = entity.AppSecret;
            model.AccessToken = entity.AccessToken;
            model.Remark = entity.Remark;
            model.Enable = entity.Enable;
            model.IsDefault = entity.IsDefault;
            model.Category = entity.Category;
            model.CreateTime = entity.CreateTime;
            model.CreateBy = entity.CreateBy;
            model.ModifyTime = entity.ModifyTime;
            model.ModifyBy = entity.ModifyBy;
            return model;
        }

數據訪問層

 public WC_OfficalAccounts GetCurrentAccount()
 {
     return Context.WC_OfficalAccounts.Where(p=>p.IsDefault).FirstOrDefault();
 }

其他代碼都由生成器生成,沒有爭議,也很簡單

生成資源服務器的鏈接

上一節我們用的是一個地址

http://ymnets.imwork.net/WC/WcChat

這次我們這個地址要稍微改變一下,讓系統知道請求者發送的請求是來自哪個公眾號:

http://ymnets.imwork.net/WC/WcChat?Id=XXXXXXXX     (XXXXXXX是我們系統自己定義的GUID)

這樣請求的時候就知道是請求哪個ID

所以我們的Post方法必須加上判斷代碼,並利用這個Id去獲取當前公眾號的信息

 [HttpPost]
        [ActionName("Index")]
        public Task<ActionResult> Post(PostModel postModel)
        {
            return Task.Factory.StartNew<ActionResult>(() =>
            {
                WC_OfficalAccountsModel model = account_BLL.GetCurrentAccount();

                //沒有參數
                if (string.IsNullOrEmpty(Request["id"]))
                {
                    return new WeixinResult("非法路徑請求!");
                }
                if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, model.Token))
                {
                    return new WeixinResult("參數錯誤!");
                }
                postModel.Token = Token;
                postModel.EncodingAESKey = EncodingAESKey; //根據自己后台的設置保持一致
                postModel.AppId = model.AppId; //根據自己后台的設置保持一致

                var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, Request["id"], 10);

                messageHandler.Execute(); //執行微信處理過程

                return new FixWeixinBugWeixinResult(messageHandler);

            }).ContinueWith<ActionResult>(task => task.Result);
        }

1.判斷URL是否是正確的

2.根據獲得ID到數據庫或者(Redis,緩存)等獲得公眾號信息

刷新Access_Token

由於我們的訪問Token默認是2個小時過時,而且我們不能時刻去微信服務器獲取

1.獲取的次數是有限制。所以我們需要保存這個Token地址,在下次過期之前更新來永遠保持Access_Token的有效

2.這里我保存在表里面,在獲取當前操作號的時候順便可以獲得這個Access_Token

他的樣子大約是這樣的:tZH82Jd6JQL6dPEU1hwhM9_jl0SrLRZV3j6-DV-EOQFUl3r37OXQNq-rpmjXEV2sVj1EQdxLotLCDMz_DdVAhZHQG6vgckOW8k90_9i24jP7giAOJM669zbiqc3HYW6wQDGeAJAYLO

如何獲得Token:這里我們使用Senparc.WeiXin SDK的方法,

Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(model.AppId, model.AppSecret).access_token;

一句話,獲得當前操作號之后,利用AppId和Appsecret,微信服務器將給你一個Token。所以這個兩個東西是要打碼的,要不很麻煩。

雖然他幫我們封裝了,但是不用他的方法,我們自己也可以直接調用微信的接口方法

            var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}",
                                    grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData());

            AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);
            return result;

這個接口,只有幾個參數,具體參數可以查看幫助文檔 傳送門 成功返回:{"access_token":"ACCESS_TOKEN","expires_in":7200}

所以我這里是一個更新當前所有公眾號的過程

 public JsonResult GetToken()
 {
     List<WC_OfficalAccountsModel> list = m_BLL.GetList(ref setNoPagerAscById, "");
     foreach (var model in list)
     {
                if (!string.IsNullOrEmpty(model.AppId) && !string.IsNullOrEmpty(model.AppSecret))
                {
                    model.AccessToken = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(model.AppId, model.AppSecret).access_token;
                    model.ModifyTime = ResultHelper.NowTime;
                    m_BLL.Edit(ref errors, model);
                }
      }
      return Json(JsonHandler.CreateMessage(1, "成批更新成功"));
 }

總結

謝謝大家


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM