在開發一套微信公眾號統一管理平台,因需要微信粉絲同步功能,結合網上看到的各種方法以及微信公眾號提供的接口,使用FluentScheduler定時框架在Asp.Net MVC中實現了多公眾號的粉絲定時同步到本地庫的功能,實現步驟分享:
一、本地數據庫的創建,因設計多公眾號的管理,分析微信接口中的用戶實體信息,創建表如下:
表結構截圖
/* Navicat MySQL Data Transfer Source Server : 127.0.0.1 Source Server Version : 50627 Source Host : 127.0.0.1:3306 Source Database : fensishenghuo Target Server Type : MYSQL Target Server Version : 50627 File Encoding : 65001 Date: 2018-04-20 11:16:26 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `wechat_user` -- ---------------------------- DROP TABLE IF EXISTS `wechat_user`; CREATE TABLE `wechat_user` ( `id` varchar(40) NOT NULL DEFAULT '' COMMENT '主鍵', `subscribe` smallint(1) DEFAULT NULL COMMENT '用戶是否訂閱該公眾號標識,值為0時,代表此用戶沒有關注該公眾號,拉取不到其余信息', `openid` varchar(40) DEFAULT NULL COMMENT '用戶的標識,對當前公眾號唯一', `nickname` varchar(30) DEFAULT '' COMMENT '用戶的昵稱', `sex` smallint(1) DEFAULT NULL COMMENT '用戶的性別,值為1時是男性,值為2時是女性,值為0時是未知', `language` varchar(20) DEFAULT NULL COMMENT '用戶的語言,簡體中文為zh_CN', `city` varchar(50) DEFAULT NULL COMMENT '用戶所在城市', `province` varchar(50) DEFAULT NULL COMMENT '用戶所在省份', `country` varchar(50) DEFAULT NULL COMMENT '用戶所在國家', `head_img_url` varchar(200) DEFAULT NULL COMMENT '用戶頭像,最后一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空。若用戶更換頭像,原有頭像URL將失效。', `subscribe_time` bigint(20) DEFAULT NULL COMMENT '用戶關注時間,為時間戳。如果用戶曾多次關注,則取最后關注時間', `union_id` varchar(40) DEFAULT NULL COMMENT '只有在用戶將公眾號綁定到微信開放平台帳號后,才會出現該字段', `remark` varchar(200) DEFAULT NULL COMMENT '公眾號運營者對粉絲的備注,公眾號運營者可在微信公眾平台用戶管理界面對粉絲添加備注', `group_id` int(6) DEFAULT NULL COMMENT '用戶所在的分組ID(兼容舊的用戶分組接口)', `tagid_list` varchar(20) DEFAULT NULL COMMENT '用戶標簽', `wechat_account_id` varchar(40) DEFAULT NULL COMMENT '該粉絲是屬於哪一個公眾號下的粉絲', `company_id` varchar(40) DEFAULT NULL COMMENT '公司主鍵,該微信用戶屬於哪一個網點的粉絲', `company_name` varchar(50) DEFAULT NULL COMMENT '公司名稱,該微信用戶屬於哪一個網點的粉絲', `user_id` varchar(255) DEFAULT NULL COMMENT '員工主鍵,該微信用戶屬於哪一個員工的粉絲', `user_name` varchar(50) DEFAULT NULL COMMENT '員工用戶名稱,該微信用戶屬於哪一個用戶的粉絲', `modified_on` date DEFAULT NULL COMMENT '更新時間', `modified_user_id` varchar(40) DEFAULT NULL COMMENT '修改人主鍵', `modified_by` varchar(40) DEFAULT NULL COMMENT '修改人', PRIMARY KEY (`id`), UNIQUE KEY `idx_wechat_account_id_openid` (`wechat_account_id`,`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信公眾號關注的用戶信息'; -- ---------------------------- -- Records of wechat_user -- ----------------------------
MySQL數據庫粉絲表的創建語句,因nickname存在表情字符,表結構需要做一下調整,將nickname的字符集改為utf8mb4_unicode_ci,當然可以將全部字符集改為utf8mb4_unicode_ci。
CREATE TABLE `wechat_user` ( `id` VARCHAR(40) NOT NULL DEFAULT '' COMMENT '主鍵', `subscribe` SMALLINT(1) NULL DEFAULT NULL COMMENT '用戶是否訂閱該公眾號標識,值為0時,代表此用戶沒有關注該公眾號,拉取不到其余信息', `openid` VARCHAR(40) NULL DEFAULT NULL COMMENT '用戶的標識,對當前公眾號唯一', `nickname` VARCHAR(30) NULL DEFAULT '' COMMENT '用戶的昵稱' COLLATE 'utf8mb4_unicode_ci', `sex` SMALLINT(1) NULL DEFAULT NULL COMMENT '用戶的性別,值為1時是男性,值為2時是女性,值為0時是未知', `language` VARCHAR(20) NULL DEFAULT NULL COMMENT '用戶的語言,簡體中文為zh_CN', `city` VARCHAR(50) NULL DEFAULT NULL COMMENT '用戶所在城市', `province` VARCHAR(50) NULL DEFAULT NULL COMMENT '用戶所在省份', `country` VARCHAR(50) NULL DEFAULT NULL COMMENT '用戶所在國家', `head_img_url` VARCHAR(200) NULL DEFAULT NULL COMMENT '用戶頭像,最后一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空。若用戶更換頭像,原有頭像URL將失效。', `subscribe_time` BIGINT(20) NULL DEFAULT NULL COMMENT '用戶關注時間,為時間戳。如果用戶曾多次關注,則取最后關注時間', `union_id` VARCHAR(40) NULL DEFAULT NULL COMMENT '只有在用戶將公眾號綁定到微信開放平台帳號后,才會出現該字段', `remark` VARCHAR(200) NULL DEFAULT NULL COMMENT '公眾號運營者對粉絲的備注,公眾號運營者可在微信公眾平台用戶管理界面對粉絲添加備注', `group_id` INT(6) NULL DEFAULT NULL COMMENT '用戶所在的分組ID(兼容舊的用戶分組接口)', `tagid_list` VARCHAR(20) NULL DEFAULT NULL COMMENT '用戶標簽', `wechat_account_id` VARCHAR(40) NULL DEFAULT NULL COMMENT '該粉絲是屬於哪一個公眾號下的粉絲', `company_id` VARCHAR(40) NULL DEFAULT NULL COMMENT '公司主鍵,該微信用戶屬於哪一個網點的粉絲', `company_name` VARCHAR(50) NULL DEFAULT NULL COMMENT '公司名稱,該微信用戶屬於哪一個網點的粉絲', `user_id` VARCHAR(255) NULL DEFAULT NULL COMMENT '員工主鍵,該微信用戶屬於哪一個員工的粉絲', `user_name` VARCHAR(50) NULL DEFAULT NULL COMMENT '員工用戶名稱,該微信用戶屬於哪一個用戶的粉絲', `modified_on` DATE NULL DEFAULT NULL COMMENT '更新時間', `modified_user_id` VARCHAR(40) NULL DEFAULT NULL COMMENT '修改人主鍵', `modified_by` VARCHAR(40) NULL DEFAULT NULL COMMENT '修改人', PRIMARY KEY (`id`), UNIQUE INDEX `idx_wechat_account_id_openid` (`wechat_account_id`, `openid`) ) COMMENT='微信公眾號關注的用戶信息' COLLATE='utf8_general_ci' ENGINE=InnoDB ;
對應mysql.data.dll驅動下載:https://files.cnblogs.com/files/hnsongbiao/MySql.Data.zip
數據庫連接字符串:
<add key="UserCenterDbConnection" value="host=127.0.0.1;uid=root;password=123456;database=shenghuo;pooling=true;charset=utf8mb4;Min Pool Size=5;Max Pool Size=50;" />
二、定時任務計划實現,微信接口的調用使用了Senparc的SDK。
//----------------------------------------------------------------------- // <copyright file="WechatUserScheduler" company="FenSiShengHuo, Ltd."> // Copyright (c) 2018 , All rights reserved. // </copyright> //----------------------------------------------------------------------- using System; using System.Collections.Generic; using FluentScheduler; namespace DotNet.WeChat.MVC.Assist { using DotNet.MVC.Infrastructure.Utilities; using DotNet.WeChat.Business; using DotNet.WeChat.Model; using Senparc.Weixin.MP.AdvancedAPIs; using Senparc.Weixin.MP.AdvancedAPIs.User; using Utilities; /// <summary> /// WechatUserScheduler /// /// 微信粉絲定時同步任務 /// /// 修改紀錄 /// /// 2018-04-18 版本:1.0 SongBiao 創建文件。 /// /// <author> /// <name>SongBiao</name> /// <date>2018-04-18</date> /// </author> /// </summary> public class WechatUserScheduler { static Registry registry; /// <summary> /// 靜態構造函數 /// </summary> static WechatUserScheduler() { registry = new Registry(); } static List<string> jobList; /// <summary> /// 終止任務 /// </summary> public static void RemoveJob() { try { foreach (var jobName in jobList) { JobManager.RemoveJob(jobName); } } catch (Exception ex) { NLogHelper.Warn(ex, "Scheduler.RemoveJob"); } } /// <summary> /// 初始化 啟動任務 /// </summary> public static void Initialize() { jobList = new List<string>(); try { WechatAccountSettingManager manager = new WechatAccountSettingManager(); List<WechatAccountSettingEntity> list = manager.GetList<WechatAccountSettingEntity>(new KeyValuePair<string, object>(WechatAccountSettingEntity.FieldEnabled, 1), new KeyValuePair<string, object>(WechatAccountSettingEntity.FieldDeletionStateCode, 0)); string jobName = string.Empty; foreach (var model in list) { jobName = "SyncUser_" + model.Id; jobList.Add(jobName); // WithName 給這個定時任務唯一ID,這個任務ID是用於顯示控制任務,后面終止任務會用到 立即執行,而且每間隔10秒(Seconds)/10分鍾執行一遍 registry.Schedule(() => SynchroWechatUser(model)).WithName(jobName).ToRunNow().AndEvery(10).Minutes(); } JobManager.Initialize(registry); } catch (Exception ex) { NLogHelper.Warn(ex, "Scheduler.Initialize"); } } /// <summary> /// 設置實體 /// </summary> /// <param name="wechatUser"></param> /// <param name="userInfo"></param> /// <param name="wechatAccountSetting"></param> private static void SetModel(WechatUserEntity wechatUser, UserInfoJson userInfo, WechatAccountSettingEntity wechatAccountSetting) { wechatUser.Subscribe = userInfo.subscribe; wechatUser.Openid = userInfo.openid; wechatUser.Nickname = userInfo.nickname; wechatUser.Sex = userInfo.sex; wechatUser.Language = userInfo.language; wechatUser.City = userInfo.city; wechatUser.Province = userInfo.province; wechatUser.Country = userInfo.country; wechatUser.HeadImgUrl = userInfo.headimgurl; wechatUser.SubscribeTime = userInfo.subscribe_time; wechatUser.UnionId = userInfo.unionid; wechatUser.Remark = userInfo.remark; wechatUser.GroupId = userInfo.groupid; wechatUser.TagidList = string.Join(",", userInfo.tagid_list); // 粉絲屬於哪一個公眾號的 wechatUser.WechatAccountId = wechatAccountSetting.Id; wechatUser.CompanyId = wechatAccountSetting.CompanyId; wechatUser.CompanyName = wechatAccountSetting.Company; } /// <summary> /// 同步微信用戶數據,按公眾號各自同步粉絲 /// 每次同步完畢記錄最后一次同步的用戶的openId,每隔一段時間繼續自動同步 /// </summary> /// <param name="wechatAccountSetting"></param> private static void SynchroWechatUser(WechatAccountSettingEntity wechatAccountSetting) { try { var accessToken = Senparc.Weixin.MP.Containers.AccessTokenContainer.TryGetAccessToken(wechatAccountSetting.AppId, wechatAccountSetting.AppSecret); OpenIdResultJson openIdResultJson; OpenIdResultJson_Data openIdResultJsonData; UserInfoJson userInfo; WechatUserManager wechatUserManager = new WechatUserManager(); WechatUserEntity wechatUserEntity = null; string tempNextOpenId = wechatAccountSetting.NextOpenid; do { openIdResultJson = UserApi.Get(accessToken, tempNextOpenId); openIdResultJsonData = openIdResultJson.data; foreach (var openId in openIdResultJsonData.openid) { // 根據openId逐個拉取用戶 userInfo = UserApi.Info(accessToken, openId); // 判斷該公眾號的openId的粉絲是否存在 wechatUserEntity = wechatUserManager.GetObjectByWechatAccountIdByOpenId(wechatAccountSetting.Id, openId); if (wechatUserEntity == null) { wechatUserEntity = new WechatUserEntity(); SetModel(wechatUserEntity, userInfo, wechatAccountSetting); wechatUserManager.Add(wechatUserEntity, false, false); } else { SetModel(wechatUserEntity, userInfo, wechatAccountSetting); wechatUserManager.Update(wechatUserEntity); } tempNextOpenId = openId; } // 執行完畢再賦值穩妥一些 上面執行過程失敗 下次會繼續 wechatAccountSetting.NextOpenid = tempNextOpenId; } while (openIdResultJson.count == 10000); } catch (Exception ex) { NLogHelper.Warn(ex, "Scheduler.SynchroWechatUser,model=" + Serializer.ToJson(wechatAccountSetting)); } finally { WechatAccountSettingManager manager = new WechatAccountSettingManager(); manager.Update(wechatAccountSetting); } } } }
三,定時任務方法的調用,直接集成到Asp.Net MVC 的Global事件里,當然也可以作為Window服務。
//----------------------------------------------------------------------- // <copyright file="MvcApplication.cs" company="FenSiShengHuo, Ltd."> // Copyright (c) 2018 , All rights reserved. // </copyright> //----------------------------------------------------------------------- using System; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace DotNet.WeChat.MVC { using DotNet.Utilities; using DotNet.WeChat.MVC.Assist; /// <summary> /// MvcApplication /// 應用全局事件 /// /// 修改記錄 /// /// 2018-04-20 版本:1.0 SongBiao 創建文件。 /// /// <author> /// <name>SongBiao</name> /// <date>2018-04-20</date> /// </author> /// </summary> public class MvcApplication : System.Web.HttpApplication { /// <summary> /// 應用啟動 /// </summary> protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // 讀取配置 ConfigurationHelper.GetConfig(); // 啟動微信粉絲定時同步任務 WechatUserScheduler.Initialize(); } /// <summary> /// 修改 標頭報文 Server /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Application_PreSendRequestHeaders(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; if (app != null && app.Context != null) { app.Context.Response.Headers.Remove("Server"); } } /// <summary> /// 應用程序結束 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Application_End(object sender, EventArgs e) { // 停止微信粉絲定時同步任務 WechatUserScheduler.RemoveJob(); } } }
頁面展示效果如上
歡迎大家參考,指點~~~