1、概述
眼前時下流行的經濟有個叫粉絲經濟,粉絲帶動收益。一個好運營良好的公眾號肯定會有一大批的粉絲團,如何挖掘粉絲來產生效益,是微信營銷的關鍵。微信公眾號后台本身提供了粉絲(用戶)與用戶分組的管理,但這些都是存放在微信的服務器,我們不好拿來分析應用。因為我們需要把我們的粉絲放在我們自己的庫中,以方便做各種應用分析。微信公眾號提供了相應的接口方便我們調用,可方便的把用戶同步到本地,這樣我們可以自己為用戶定義更多的信息,以及與本地的業務更好的對接起來。
2、本地存放微信粉絲與分組的表結構
在微信開發過程中,一般定義表結構我們可以直接把微信返回的對象數據如這兒的用戶返回的信息直接存放到我們的庫表結構,結構類型可以一至,再加上我們需要額外添加的擴展字段做業務上的處理。如下是我們建立的微信用戶與分組的表結構。
3、主要接口實現方式
本文中所有接口的實現方式我們使用了開源的Senparc.Weixin提供的專業的微信操作SDK來快速完成操作。Senparc.Weixin SDK相關文章可參考文章末尾的參考文章。
3.1、同步指定用戶到本地
微信的用戶信息隨時在變,如:用戶昵稱,地址,頭像什么的隨時在修改,我們本地庫中的數據也應該變才可以。如果用戶組非常多,如幾萬的粉絲,如果每次都通過一鍵同步所有用戶來做維護,效率會非常低,這時我們就需要針對特定的用戶手動同步其用戶基本信息。這時我們就需要使用微信提供的獲取用戶基本信息接口來同步指定用戶。在關注者與公眾號產生消息交互后,公眾號可獲得關注者的OpenID(加密后的微信號,每個用戶對每個公眾號的OpenID是唯一的。對於不同公眾號,同一用戶的openid不同)。公眾號可通過本接口來根據OpenID獲取用戶基本信息,包括昵稱、頭像、性別、所在城市、語言和關注時間。
接口調用請求說明
http請求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
參數說明
參數 是否必須 說明
access_token 是 調用接口憑證
openid 是 普通用戶的標識,對當前公眾號唯一
lang 否 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語
具體的接口詳細介紹及使用方法可參考微信公眾平台技術文檔-獲取用戶基本信息
使用Senparc.Weixin SDK中的Senparc.Weixin.MP.AdvancedAPIs.UserApi.BatchGetUserInfo批量獲取用戶基本信息,就可以手動同步選定的用戶列表。其實BatchGetUserInfo接口參考:
/// <summary>
/// 批量獲取用戶基本信息
/// </summary>
/// <param name="accessTokenOrAppId"></param>
/// <param name="userList"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
public static BatchGetUserInfoJson BatchGetUserInfo(string accessTokenOrAppId, List<BatchGetUserInfoData> userList, int timeOut = Config.TIME_OUT)
{
return ApiHandlerWapper.TryCommonApi(accessToken =>
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={0}",
accessToken);
var data = new
{
user_list = userList,
};
return CommonJsonSend.Send<BatchGetUserInfoJson>(accessToken, url, data, timeOut: timeOut);
}, accessTokenOrAppId);
}
具體業務實現代碼參考:
/// <summary>
/// 手動同步用戶
/// </summary>
/// <param name="userIds">待同步的用戶主鍵列表(逗號分隔)</param>
/// <returns></returns>
[HttpPost]
[ValidateInput(false)]
[LoginAuthorize]
public ActionResult SyncUser(string userIds)
{
int returnValue = 0;
UserInfo curUserInfo = ManageProvider.Provider.Current();
if (!string.IsNullOrWhiteSpace(userIds))
{
//填充數據
string[] arrs = userIds.Split(',');
List<BatchGetUserInfoData> list = new List<BatchGetUserInfoData>();
foreach (var m in arrs)
{
list.Add(new BatchGetUserInfoData()
{
openid = m,
lang = "zh-CN",
LangEnum = Senparc.Weixin.Language.zh_CN
});
}
//批量同步數據
WeixinOfficialAccountEntity accountModel = RDIFrameworkService.Instance.WeixinBasicService.GetCurrentOfficialAccountEntity(curUserInfo);
try
{
var batchList = Senparc.Weixin.MP.AdvancedAPIs.UserApi.BatchGetUserInfo(accountModel.AccessToken, list);
foreach (var info in batchList.user_info_list)
{
WeixinUserEntity userModel = RDIFrameworkService.Instance.WeixinBasicService.GetUserEntity(curUserInfo, info.openid);
if (userModel != null)
{
userModel.City = info.city;
userModel.OpenId = info.openid;
userModel.Id = info.openid;
userModel.HeadImgUrl = info.headimgurl;
userModel.Subscribe = info.subscribe;
userModel.SubscribeTime = DateTimeHelper.GetTimeByLong(info.subscribe_time * 1000);//注意:單位為秒,不是毫秒,要轉換為毫秒要乘以1000,這個官網開發文檔沒有說明。
userModel.Language = info.language;
userModel.NickName = info.nickname;
userModel.Province = info.province;
userModel.Sex = info.sex;
userModel.UnionId = info.unionid;
userModel.Contry = info.country;
userModel.Remark = info.remark;
userModel.GroupId = BusinessLogic.ConvertToString(info.groupid);
returnValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateUser(userModel);
}
}
}
catch (Exception ex)
{
if (ex.Message.Contains("找不到方法"))
{
return Content(new JsonMessage { Success = false, Data = "-1", Type = ResultType.Error, Message = "Token已過期..." }.ToString());
}
else
{
return Content(new JsonMessage { Success = false, Data = "-1", Type = ResultType.Error, Message = RDIFramework.Utilities.RDIFrameworkMessage.MSG3020 + "錯誤信息:" + ex.Message }.ToString());
}
}
return Content(returnValue > 0
? new JsonMessage { Success = true, Data = "1", Type = ResultType.Success, Message = RDIFrameworkMessage.MSG3010 }.ToString()
: new JsonMessage { Success = false, Data = "0", Type = ResultType.Warning, Message = RDIFrameworkMessage.MSG3020 }.ToString());
}
else
{
return Content(new JsonMessage { Success = false, Data = "-1", Type = ResultType.Error, Message = RDIFramework.Utilities.RDIFrameworkMessage.MSG3020 }.ToString());
}
}
3.2、一鍵同步所有用戶到本地
在進行微信公眾號開發之初,我們公眾號已經有了一些粉絲數,這時我們可能就需要一鍵把這些用戶全部同步到我們本地庫。要做到一鍵同步需要調用兩個接口,兩步來完成。
具體的接口詳細介紹及使用方法可參考微信公眾平台技術文檔-獲取用戶列表
第一步:獲取關注列列表OpenId,接口為:
http請求方式: GET(請使用https協議)
https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
參數 是否必須 說明
access_token 是 調用接口憑證
next_openid 是 第一個拉取的OPENID,不填默認從頭開始拉取
公眾號可通過本接口來獲取帳號的關注者列表,關注者列表由一串OpenID(加密后的微信號,每個用戶對每個公眾號的OpenID是唯一的)組成。一次拉取調用最多拉取10000個關注者的OpenID,可以通過多次拉取的方式來滿足需求。
當公眾號關注者數量超過10000時,可通過填寫next_openid的值,從而多次拉取列表的方式來滿足需求。具體而言,就是在調用接口時,將上一次調用得到的返回中的next_openid值,作為下一次調用中的next_openid值。
第二步:通過返回的用戶OpenId列表,得到第一個用戶的基本信息再同步到本地庫中,接口為:
接口調用請求說明
http請求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
參數說明
參數 是否必須 說明
access_token 是 調用接口憑證
openid 是 普通用戶的標識,對當前公眾號唯一
lang 否 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語
具體代碼參考,使用Senparc.Weixin SDK中的Senparc.Weixin.MP.AdvancedAPIs.UserApi.Get得到關注者的OpenId列表,再遍歷得到的OpenId列表得到用戶的基本信息,接口為:Senparc.Weixin.MP.AdvancedAPIs.UserApi.Info
其中UserApi.Get方法實現代碼參考:
/// <summary>
/// 獲取關注者OpenId信息
/// </summary>
/// <param name="accessTokenOrAppId">AccessToken或AppId(推薦使用AppId,需要先注冊)</param>
/// <param name="nextOpenId"></param>
/// <returns></returns>
[ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "UserApi.Get", true)]
public static OpenIdResultJson Get(string accessTokenOrAppId, string nextOpenId)
{
return ApiHandlerWapper.TryCommonApi(accessToken =>
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/get?access_token={0}", accessToken.AsUrlData());
if (!string.IsNullOrEmpty(nextOpenId))
{
url += "&next_openid=" + nextOpenId;
}
return HttpUtility.Get.GetJson<OpenIdResultJson>(url);
}, accessTokenOrAppId);
}
通過下面的代碼就可以詳細明白操作的步驟,除去相關的業務應用,真正的代碼很少。
/// <summary>
/// 一鍵同步當前操作公眾號下所有用戶
/// 說明:一般用於粉絲沒有包含在本地庫中的同步到同地庫中
/// </summary>
/// <returns></returns>
[HttpPost]
[ValidateInput(false)]
[LoginAuthorize]
public ActionResult SyncAllUser()
{
UserInfo curUserInfo = ManageProvider.Provider.Current();
int returnAddValue = 0, returnUpdateValue = 0;
try
{
//批量同步數據
WeixinOfficialAccountEntity accountEntity = RDIFrameworkService.Instance.WeixinBasicService.GetCurrentOfficialAccountEntity(curUserInfo);
//獲取關注着OpenId信息
var openIdResultJson = Senparc.Weixin.MP.AdvancedAPIs.UserApi.Get(accountEntity.AccessToken, "");
if (openIdResultJson != null && openIdResultJson.count > 0)
{
foreach (string openId in openIdResultJson.data.openid)
{
UserInfoJson remoteUserInfo = Senparc.Weixin.MP.AdvancedAPIs.UserApi.Info(accountEntity.AccessToken, openId, Senparc.Weixin.Language.zh_CN);
var localUserEntity = RDIFrameworkService.Instance.WeixinBasicService.GetCurOfficialAccountUserByOpenId(curUserInfo, accountEntity.Id, openId);
if (localUserEntity == null)
{
if (remoteUserInfo != null)
{
localUserEntity = new WeixinUserEntity
{
OpenId = remoteUserInfo.openid,
City = remoteUserInfo.city,
Id = remoteUserInfo.openid,
OfficialAccountId = accountEntity.Id,
HeadImgUrl = remoteUserInfo.headimgurl,
SubscribeTime = DateTimeHelper.GetTimeByLong(remoteUserInfo.subscribe_time * 1000),//注意:單位為秒,不是毫秒,要轉換為毫秒要乘以1000,這個官網開發文檔沒有說明。
Language = remoteUserInfo.language,
Subscribe = remoteUserInfo.subscribe,
NickName = remoteUserInfo.nickname,
Province = remoteUserInfo.province,
Sex = remoteUserInfo.sex,
Contry = remoteUserInfo.country,
Remark = remoteUserInfo.remark,
UnionId = remoteUserInfo.unionid,
GroupId = BusinessLogic.ConvertToString(remoteUserInfo.groupid, null)
};
returnAddValue += (string.IsNullOrEmpty(RDIFrameworkService.Instance.WeixinBasicService.AddUser(localUserEntity)) ? 0 : 1);
}
}
else
{
//取消訂閱后又重新訂閱了,需要修改本地
if (remoteUserInfo != null && localUserEntity.Subscribe != remoteUserInfo.subscribe)
{
localUserEntity.City = remoteUserInfo.city;
localUserEntity.OpenId = remoteUserInfo.openid;
localUserEntity.Id = remoteUserInfo.openid;
localUserEntity.HeadImgUrl = remoteUserInfo.headimgurl;
localUserEntity.Subscribe = remoteUserInfo.subscribe;
localUserEntity.SubscribeTime = DateTimeHelper.GetTimeByLong(remoteUserInfo.subscribe_time * 1000);//注意:單位為秒,不是毫秒,要轉換為毫秒要乘以1000,這個官網開發文檔沒有說明。
localUserEntity.Language = remoteUserInfo.language;
localUserEntity.NickName = remoteUserInfo.nickname;
localUserEntity.Province = remoteUserInfo.province;
localUserEntity.Sex = remoteUserInfo.sex;
localUserEntity.UnionId = remoteUserInfo.unionid;
localUserEntity.Contry = remoteUserInfo.country;
localUserEntity.Remark = remoteUserInfo.remark;
localUserEntity.GroupId = BusinessLogic.ConvertToString(remoteUserInfo.groupid);
returnUpdateValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateUser(localUserEntity);
}
}
}
}
}
catch (Exception ex)
{
if (ex.Message.Contains("找不到方法"))
{
return Content(new JsonMessage { Success = false, Data = "-1", Type = ResultType.Error, Message = "Token已過期..." }.ToString());
}
else
{
return Content(new JsonMessage { Success = false, Data = "-1", Type = ResultType.Error, Message = RDIFramework.Utilities.RDIFrameworkMessage.MSG3020 + "錯誤信息:" + ex.Message }.ToString());
}
}
return Content((returnAddValue > 0 || returnUpdateValue > 0)
? new JsonMessage { Success = true, Data = "1", Type = ResultType.Success, Message = RDIFrameworkMessage.MSG3010 + ",新增:" + returnAddValue.ToString() + "個粉絲,修改:" + returnUpdateValue.ToString() + " 個用戶。" }.ToString()
: new JsonMessage { Success = false, Data = "0", Type = ResultType.Warning, Message = "操作完成,無新增,無修改!" }.ToString());
}
4、關注與取消關注時自動同步本地用戶情況
上面的方式都是對已經關注的用戶做同步處理。如果我們在用戶關注的同時就自動把關注的用戶同步到本地庫,這樣就更加的方便。
當我們關注某些微信公眾號的時候,有的公眾號會立即給我們回復一條信息。這是如何實現的呢?原來在用戶在關注與取消關注公眾號時,微信會把這個事件推送到開發者填寫的URL。方便開發者給用戶下發歡迎消息或者做帳號的解綁。為保護用戶數據隱私,開發者收到用戶取消關注事件時需要刪除該用戶的所有信息。
我們是基於微信的第三方平台來做二次開發,開發的依據必須是官方的API也就是開發文檔。所以,我們要先查詢開發文檔來找到關注和取關事件說明。訪問url為:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140454
我們的公眾號服務接收到微信服務器回傳的xml信息,從中獲取MsgType和Event的值,可以區分出用戶的關注和取消關注的行為,對不同的行為程序可以做出不同的響應。
我們直接使用Senparc.Weixin SDK提供的接口,重載OnEvent_SubscribeRequest-訂閱與OnEvent_UnsubscribeRequest-取消訂閱事件處理即可。
4.1、訂閱(關注)時的處理
關注事件代碼參考:
/// <summary>
/// 訂閱(關注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = "歡迎關注!";
return responseMessage;
}
上面的代碼只要用戶關注了關注號就會自動回復:歡迎關注!
如果要回復圖文模式如下圖所示:
圖文回復代碼參考:
/// <summary>
/// 訂閱(關注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageNews>();
responseMessage.Articles.Add(new Article()
{
Title = "國思公眾號",
Description = "歡迎關注國思軟件公眾號,更多內容移步到官網,多謝!",
PicUrl = "http://www.rdiframework.net/img/weixing-ma.png",
Url = "http://www.rdiframework.net/"
});
return responseMessage;
}
上面的代碼就可以實現關注成功后自動圖文回復。我們還可以在關注時處理相關的業務邏輯,如:關注成功自動把關注用戶同步到本地庫。同樣的在關注事件中處理,代碼參考:
/// <summary>
/// 訂閱(關注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
//獲得當前公眾號
WeixinOfficialAccountEntity account = RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountEntity();
//將用戶提取到本地數據庫
WeixinUserEntity userEntity = new WeixinUserEntity();
UserInfoJson userJson = UserApi.Info(account.AccessToken, requestMessage.FromUserName);
userEntity.Id = BusinessLogic.NewGuid();
if (!string.IsNullOrEmpty(userJson.openid))
{
userEntity.Id = userJson.openid;
}
userEntity.OpenId = userJson.openid;
userEntity.NickName = userJson.nickname;
userEntity.Sex = userJson.sex;
userEntity.Language = userJson.language;
userEntity.City = userJson.city;
userEntity.Province = userJson.province;
userEntity.Contry = userJson.country;
userEntity.HeadImgUrl = userJson.headimgurl;
userEntity.SubscribeTime = DateTimeHelper.GetTimeByLong(userJson.subscribe_time * 1000);//注意:單位為秒,不是毫秒,要轉換為毫秒要乘以1000,這個官網開發文檔沒有說明。
userEntity.UnionId = userJson.unionid;
userEntity.Remark = userJson.remark;
userEntity.GroupId = userJson.groupid.ToString();
userEntity.TagIdList = string.Join(",", userJson.tagid_list.ToArray());
userEntity.Subscribe = userJson.subscribe;
userEntity.OfficialAccountId = account.Id;
userEntity.CreateBy = "WeiXinServer";
string returnValue = RDIFrameworkService.Instance.WeixinBasicService.AddUser(userEntity); //增加用戶
//訂閱回復
var responseMessage = CreateResponseMessage<ResponseMessageNews>();
responseMessage.Articles.Add(new Article()
{
Title = "國思公眾號",
Description = "歡迎關注國思軟件公眾號,更多內容移步到官網,多謝!",
PicUrl = "http://www.rdiframework.net/img/weixing-ma.png",
Url = "http://www.rdiframework.net/"
});
return responseMessage;
}
4.2、取消關注時的處理
取消關注我們可以對事件OnEvent_UnsubscribeRequest做處理。參考代碼:
/// <summary>
/// 退訂
/// 實際上用戶無法收到非訂閱賬號的消息,所以這里可以隨便寫。
/// unsubscribe事件的意義在於及時刪除網站應用中已經記錄的OpenID綁定,消除冗余數據。並且關注用戶流失的情況。
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "有空再來";
return responseMessage;
}
在用戶取消關注事件中,我們還可以加入我們自己的業務邏輯,對取消關注的用戶做業務上的處理,用戶可根據實際的情況來增加自己要處理的業務。
5、用戶分組管理
新關注的用戶默認會自動分到“未分組”分組中,我們可以根據實現需要建立我們自己的分組並把用戶移動到對應的分組中,以方便管理與業務應用的處理。
5.1、創建分組
每個帳號下最多只能創建1000個分組,接口調用說明:
http請求方式: POST(請使用https協議)https://api.weixin.qq.com/shakearound/device/group/add?access_token=ACCESS_TOKEN
POST數據格式:json
POST數據例子:
{
"group_name":"test"
}
Senparc.Weixin SDK對應代碼:
Senparc.Weixin.MP.AdvancedAPIs.GroupsApi.Create("accessToken", "分組名稱");
5.2、修改分組
編輯設備分組信息,目前只能修改分組名。接口調用說明:
http請求方式: POST(請使用https協議)https://api.weixin.qq.com/shakearound/device/group/update?access_token=ACCESS_TOKEN
POST數據格式:json
POST數據例子:
{
"group_id":123,
"group_name":"test update"
}
Senparc.Weixin SDK對應代碼:
Senparc.Weixin.MP.AdvancedAPIs.GroupsApi.Update("accessToken", "分組Id", "分組名稱");
5.3、刪除分組
刪除分組,對應分組中的用戶會自動移動到“未分組”分組中。接口調用說明:
http請求方式: POST(請使用https協議)https://api.weixin.qq.com/shakearound/device/group/delete?access_token=ACCESS_TOKEN
POST數據格式:json
POST數據例子:
{
"group_id":123
}
Senparc.Weixin SDK對應代碼:
Senparc.Weixin.MP.AdvancedAPIs.GroupsApi.Delete("accessToken", "分組Id");
5.4、查詢分組列表
查詢賬號下所有的分組。接口調用說明:
http請求方式: POST(請使用https協議)https://api.weixin.qq.com/shakearound/device/group/getlist?access_token=ACCESS_TOKEN
POST數據格式:json
POST數據例子:
{
"begin": 0,
"count" 10
}
Senparc.Weixin SDK對應代碼:
Senparc.Weixin.MP.AdvancedAPIs.GroupsApi.Get("accessToken");
5.5、移動用戶到指定分組
移動用戶分組對應的Senparc.Weixin SDK代碼:
Senparc.Weixin.MP.AdvancedAPIs.MemberUpdate(accessTokenOrAppId, openId, toGroupId, timeOut = 10000);
6、功能展示
6.1、用戶組管理功能展示
6.2、用戶列表功能展示
參考文章
RDIFramework.NET — 基於.NET的快速信息化系統開發框架 — 系列目錄
RDIFramework.NET ━ .NET快速信息化系統開發框架 ━ 工作流程組件介紹
RDIFramework.NET框架SOA解決方案(集Windows服務、WinForm形式與IIS形式發布)-分布式應用
RDIFramework.NET代碼生成器全新V3.5版本發布-重大升級
一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,大家可以通過下面的地址了解詳情。
RDIFramework.NET官方網站:http://www.rdiframework.net/
RDIFramework.NET官方博客:http://blog.rdiframework.net/
同時需要說明的,以后的所有技術文章以官方網站為准,歡迎大家收藏!
RDIFramework.NET框架由專業團隊長期打造、一直在更新、一直在升級,請放心使用!
歡迎關注RDIFramework.net框架官方公眾微信(微信號:guosisoft),及時了解最新動態。
掃描二維碼立即關注