眼前時下流行的經濟有個叫粉絲經濟,粉絲帶動收益。一個好運營良好的公衆號確定會有一大批的粉絲團,如何挖掘粉絲來產生效益,是微信營銷的關鍵。微信公衆號後臺自己提供了粉絲(用戶)與用戶分組的管理,但這些都是存放在微信的服務器,咱們很差拿來分析應用。由於咱們須要把咱們的粉絲放在咱們本身的庫中,以方便作各類應用分析。微信公衆號提供了相應的接口方便咱們調用,可方便的把用戶同步到本地,這樣咱們能夠本身爲用戶定義更多的信息,以及與本地的業務更好的對接起來。git
在微信開發過程當中,通常定義表結構咱們能夠直接把微信返回的對象數據如這兒的用戶返回的信息直接存放到咱們的庫表結構,結構類型能夠一至,再加上咱們須要額外添加的擴展字段作業務上的處理。以下是咱們創建的微信用戶與分組的表結構。github
本文中全部接口的實現方式咱們使用了開源的Senparc.Weixin提供的專業的微信操做SDK來快速完成操做。Senparc.Weixin SDK相關文章可參考文章末尾的參考文章。數據庫
微信的用戶信息隨時在變,如:用戶暱稱,地址,頭像什麼的隨時在修改,咱們本地庫中的數據也應該變才能夠。若是用戶組很是多,如幾萬的粉絲,若是每次都經過一鍵同步全部用戶來作維護,效率會很是低,這時咱們就須要針對特定的用戶手動同步其用戶基本信息。這時咱們就須要使用微信提供的獲取用戶基本信息接口來同步指定用戶。在關注者與公衆號產生消息交互後,公衆號可得到關注者的OpenID(加密後的微信號,每一個用戶對每一個公衆號的OpenID是惟一的。對於不一樣公衆號,同一用戶的openid不一樣)。公衆號可經過本接口來根據OpenID獲取用戶基本信息,包括暱稱、頭像、性別、所在城市、語言和關注時間。json
接口調用請求說明
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 英語
複製代碼
具體的接口詳細介紹及使用方法可參考微信公衆平臺技術文檔-獲取用戶基本信息api
使用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());
}
}
複製代碼
在進行微信公衆號開發之初,咱們公衆號已經有了一些粉絲數,這時咱們可能就須要一鍵把這些用戶所有同步到咱們本地庫。要作到一鍵同步須要調用兩個接口,兩步來完成。 具體的接口詳細介紹及使用方法可參考微信公衆平臺技術文檔-獲取用戶列表 第一步:獲取關注列列表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值。app
第二步:經過返回的用戶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());
}
複製代碼
上面的方式都是對已經關注的用戶作同步處理。若是咱們在用戶關注的同時就自動把關注的用戶同步到本地庫,這樣就更加的方便。 當咱們關注某些微信公衆號的時候,有的公衆號會當即給咱們回覆一條信息。這是如何實現的呢?原來在用戶在關注與取消關注公衆號時,微信會把這個事件推送到開發者填寫的URL。方便開發者給用戶下發歡迎消息或者作賬號的解綁。爲保護用戶數據隱私,開發者收到用戶取消關注事件時須要刪除該用戶的全部信息。
咱們是基於微信的第三方平臺來作二次開發,開發的依據必須是官方的API也就是開發文檔。因此,咱們要先查詢開發文檔來找到關注和取關事件說明。訪問url爲:mp.weixin.qq.com/wiki?t=reso… 咱們的公衆號服務接收到微信服務器回傳的xml信息,從中獲取MsgType和Event的值,能夠區分出用戶的關注和取消關注的行爲,對不一樣的行爲程序能夠作出不一樣的響應。
咱們直接使用Senparc.Weixin SDK提供的接口,重載OnEvent_SubscribeRequest-訂閱與OnEvent_UnsubscribeRequest-取消訂閱事件處理便可。
關注事件代碼參考:
/// <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;
}
複製代碼
取消關注咱們能夠對事件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;
}
複製代碼
在用戶取消關注事件中,咱們還能夠加入咱們本身的業務邏輯,對取消關注的用戶作業務上的處理,用戶可根據實際的狀況來增長本身要處理的業務。
新關注的用戶默認會自動分到「未分組」分組中,咱們能夠根據實現須要創建咱們本身的分組並把用戶移動到對應的分組中,以方便管理與業務應用的處理。
每一個賬號下最多隻能建立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", "分組名稱");
編輯設備分組信息,目前只能修改分組名。接口調用說明:
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", "分組名稱");
複製代碼
刪除分組,對應分組中的用戶會自動移動到「未分組」分組中。接口調用說明:
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");
查詢帳號下全部的分組。接口調用說明:
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");
移動用戶分組對應的Senparc.Weixin SDK代碼:
Senparc.Weixin.MP.AdvancedAPIs.MemberUpdate(accessTokenOrAppId, openId, toGroupId, timeOut = 10000);
複製代碼
RDIFramework.NET — 基於.NET的快速信息化系統開發框架 — 系列目錄
RDIFramework.NET ━ .NET快速信息化系統開發框架 ━ 工做流程組件介紹
RDIFramework.NET框架SOA解決方案(集Windows服務、WinForm形式與IIS形式發佈)-分佈式應用
RDIFramework.NET代碼生成器全新V3.5版本發佈-重大升級
一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,你們能夠經過下面的地址瞭解詳情。
RDIFramework.NET官方網站:www.rdiframework.net/
RDIFramework.NET官方博客:blog.rdiframework.net/
同時須要說明的,之後的全部技術文章以官方網站爲準,歡迎你們收藏!
RDIFramework.NET框架由專業團隊長期打造、一直在更新、一直在升級,請放心使用!
歡迎關注RDIFramework.net框架官方公衆微信(微信號:guosisoft),及時瞭解最新動態。
掃描二維碼當即關注