Java 微信公众号迁移
背景:公众号换主体,要迁移,粉丝(openId)的业务数据要做处理.
第一步:参照我的另一篇文章,Java 导出微信公众号粉丝。
第二部:数据处理(master-worker模式)
程序主入口:Main
我导出来的粉丝文件格式是:
{
"info":[
{"openId":"ogVous494ltuNmO4zHb1seHeGLSk"}
..... 1万条
]
}
package changeOpenId; import java.util.List;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.odao.weixin.api.support.AccessTokenKit;
import com.odao.weixin.site.cases2017.change.service.ChangeService;
import com.odao.weixin.site.cases2017.push.entity.JsonDataReadUtil; /**
* 多线程转换openId
* @author wangfj
*/
public class ChangeMain {
@SuppressWarnings({ "unchecked", "static-access", "resource" })
public static void main(String[] args) throws Exception {
ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"odao-weixin-site-servlet.xml"});
String token = AccessTokenKit.getTokenNew("APPID", "APP秘钥");
String accesstoken = (String) ((Map) JSON.parseObject(token, Map.class)).get("access_token");
//根据粉丝文件来读取数据
JSONObject openIdJson = JsonDataReadUtil.getReadJsonByPath("第一步导出来的文件");
String info= openIdJson.get("info").toString();
JSONArray jsonArr = JSONObject.parseArray(info);
List<Map<String,Object>> list = jsonArr.toJavaObject(jsonArr, List.class);
//如果你们是直接操作数据库,可以直接写一个业务类,从数据库获取要转换的数据,如下:
/*ChangeService changeService = (ChangeService) appContext.getBean("changeService");
List<Map<String,Object>> list = changeService.queryRecord(40000,50000);*/
//构造master
ChangeMaster master = new ChangeMaster(new ChangeWorker(),100,accesstoken,appContext);//第二个参数100(worker工作线程数),这个根据你们自己的需求定
for(int i=0;i<list.size();i+=100){//微信转换openId接口,腾讯说是一次只能处理100条
List<Map<String,Object>> newList = list.subList(i, (i+100)>list.size()?list.size():(i+100));
master.submit(newList);
}
//多线程执行
master.execute();
while(true){
if(master.isComplate()){
long allTime = master.getResult();
System.out.println("主线程执行完毕,总耗时:"+allTime+"毫秒");
break;
}
}
} }
Master:
package changeOpenId; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.springframework.context.ApplicationContext; /**
* Master任务指派者,分发者
* @author wangfj
*/
public class ChangeMaster{ private ConcurrentLinkedQueue<List<Map<String,Object>>> workerQueue = new ConcurrentLinkedQueue<List<Map<String,Object>>>(); //存放所有的工作者
private HashMap<String, Thread> workers = new HashMap<String, Thread>(); //存放数据的结果集
private ConcurrentHashMap<String,Object> resultMap = new ConcurrentHashMap<String, Object>(); //构造master
public ChangeMaster(Worker worker,int workerCount,String accessToken,ApplicationContext appContext){
worker.setApplicationContext(appContext);
worker.setWorkerQueue(this.workerQueue);
worker.setAccessToken(accessToken);
worker.setResultMap(resultMap);
for(int i=0;i<workerCount;i++){
this.workers.put("执行任务worker"+i,new Thread(worker,"子线程"+i));
}
} //提交
public void submit(List<Map<String,Object>> list){
this.workerQueue.add(list);
} //执行
public void execute(){
for(Map.Entry<String,Thread> me :workers.entrySet()){
me.getValue().start();
}
} //获得执行结果集
public long getResult(){
long result = 0l;
for(Map.Entry<String,Object> me :resultMap.entrySet()){
result += (Long)me.getValue();
}
return result;
} //所有子线程是否执行完毕
public boolean isComplate() {
for(Map.Entry<String,Thread> me :workers.entrySet()){
if(Thread.State.TERMINATED != me.getValue().getState()){
return false;
}
}
return true;
}
}
Worker(转换openId核心代码):
package changeOpenId; import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.odao.weixin.site.cases2017.change.service.ChangeService; @Service
public class ChangeWorker extends Worker{ public static String url = "http://api.weixin.qq.com/cgi-bin/changeopenid?access_token="; //微信提供中的openId转换接口 public static long handle(List<Map<String,Object>> list,String accessToken,ApplicationContext appContext) throws Exception {
ChangeService changeService = (ChangeService) appContext.getBean("changeService");//我处理数据的业务类
long end = System.currentTimeMillis();
JSONObject params = new JSONObject();
params.put("from_appid", "xxxx");//此处from_appid为原帐号的appid ArrayList<String> openIds = new ArrayList<String>();
for(int i=0;i<list.size();i++){
openIds.add(list.get(i).get("openId").toString());
} params.put("openid_list", openIds);//需要转换的openid,即第1步中拉取的原帐号用户列表,这些必须是旧账号目前关注的才行,否则会出错;一次最多100个,不能多。格式:["openIdA","openIdB"] String reslut = JsonSMS(params.toString(),accessToken);
JSONObject json = (JSONObject) JSONObject.parse(reslut);
if("ok".equals(json.getString("errmsg"))){
String result_list = json.get("result_list").toString();
JSONArray arr= JSONObject.parseArray(result_list);
List<Map<String,Object>> obj = arr.toJavaObject(arr, List.class);
if(!obj.get(0).get("err_msg").equals("ori_openid error")){
List<Map<String,Object>> openIdObj = arr.toJavaObject(arr, List.class);
changeService.batchUpdateAccountsMapping(openIdObj);
}
}else{
System.out.println("请求微信转换接口返回异常");
}
return System.currentTimeMillis()-end;
} public static String JsonSMS(String postData, String token) {
String result = "";
try {
//发送POST请求
URL urls = new URL(url.concat(token));
HttpURLConnection conn = (HttpURLConnection) urls.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Length", "" + postData.length());
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
out.write(postData);
out.flush();
out.close();
//获取响应状态
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
System.out.println("connect failed!");
return "";
}
//获取响应内容体
String line;
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
while ((line = in.readLine()) != null) {
result += line + "\n";
}
in.close();
} catch (IOException e) {
e.printStackTrace(System.out);
}
return result;
}
}
package changeOpenId; import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.springframework.context.ApplicationContext; public class Worker implements Runnable{ private ConcurrentLinkedQueue<List<Map<String,Object>>> workerQueue; private ConcurrentHashMap<String,Object> resultMap; private ApplicationContext appContext; private String accessToken; public void setWorkerQueue(ConcurrentLinkedQueue<List<Map<String,Object>>> workerQueue) {
this.workerQueue = workerQueue;
} public void setResultMap(ConcurrentHashMap<String, Object> resultMap) {
this.resultMap = resultMap;
} public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
} @Override
public void run() {
while(true){
List<Map<String,Object>> input = this.workerQueue.poll();
if(input==null) break;
try {
Random random = new Random();
long time = ChangeWorker.handle(input,accessToken,appContext);
resultMap.put(String.valueOf(random.nextInt(100)), time);
} catch (Exception e) {
e.printStackTrace();
}
}
} @SuppressWarnings("unused")
private static void handle(List<String> list) {} public void setApplicationContext(ApplicationContext appContext) {
this.appContext = appContext; }
}
处理数据的业务类:
package com.odao.weixin.site.cases2017.change.service; import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; @Service
public class ChangeService { static final Logger logger = LoggerFactory.getLogger(ChangeService.class); @Autowired
private JdbcTemplate jdbcTemplateWebSiteActivityDB; /**
* 批量更新玩家openId
* @param list
*/
public void batchUpdateAccountsMapping(final List<Map<String,Object>> list) {
String sql = "update 表名 set newOpenId=? ,createTime=getdate() where openId=?";
jdbcTemplateWebSiteActivityDB.batchUpdate(sql, new BatchPreparedStatementSetter() {
public int getBatchSize() {
return list.size();
//这个方法设定更新记录数,通常List里面存放的都是我们要更新的,所以返回list.size();
}
public void setValues(java.sql.PreparedStatement ps, int i) throws SQLException {
try{
ps.setString(1, list.get(i).get("new_openid").toString());
ps.setString(2, list.get(i).get("ori_openid").toString());
}catch(Exception e){ }
}
});
System.out.println(Thread.currentThread().getName()+"成功改变条数:"+list.size());
}
}
业务类操作的表字段最好加上索引,基本上几秒钟几万数据就跑完了。
Java 微信公众号迁移的更多相关文章
- Java微信公众号安全模式消息解密
这篇文章主要为大家详细介绍了Java微信公众号安全模式消息解密,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.微信公众平台下载解密工具,导入项目中,根据demo解密消息 public stat ...
- Java 微信公众号上传永久素材的方法
Java 微信公众号上传永久素材的方法 学习了:http://blog.csdn.net/u013791374/article/details/53258275 膜拜一下,源码如下: @Request ...
- Java微信公众号开发梳理
Java微信公众号开发梳理 现在微信公众平台的开发已经越来越普遍,这次开发需要用到微信公众平台.因此做一个简单的记录,也算是给那些没踩过坑的童鞋一些启示吧.我将分几块来简单的描述一下,之后会做详细的说 ...
- Java微信公众号开发
微信公众平台是腾讯为了让用户申请和管理微信公众账号而推出的一个web平台.微信公众账号的种类可以分为3种,并且一旦选定不可更改.按照功能的限制从小到大依次为:订阅号.服务号.企业号.个人只能注册订阅号 ...
- [特别公告]RDIFramework.NET微信公众号迁移通知
亲爱的伙伴们: 非常感谢您们一直以来对RDIFramework.NET开发框架的关注和支持! 为了进一步完善各项功能,能给大家提供更专业.更官方准确的框架资讯,提供更优质的框架合作服务,我们的微信公众 ...
- JAVA微信公众号通过openid发送模板消息~
1,问题产生 在微信公众号开发过程中,我们有时候做不同权限的时候,比如在注册的时候,需要审核,然后我们要想办法让对方知道审核的结果.这时候我们可以通过模板消息来通知. 2,第一步,首先在微信公众号上获 ...
- JAVA微信公众号网页开发 —— 用户授权获取openid
官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 HttpClientUtil.java packa ...
- java微信公众号JSAPI支付以及所遇到的坑
上周做了个支付宝微信扫码支付,今天总结一下.微信相比支付宝要麻烦许多 由于涉及到代理商,没办法,让我写个详细的申请流程,懵逼啊. 笔记地址 http://note.youdao.com/notesha ...
- Java微信公众号开发----定时获取access_token并保存到redis中
本人原本是想做微信公众号菜单的创建修改删除等操作的,但是发现需要access_token,通过阅读文档,发现文档要求有以下几点: 1.access_token 获取后有效期是2小时 2.access_ ...
随机推荐
- Verilog定义计算位宽的函数clogb2
在很多情况下要计算输入输出的位宽,比如你写一个8*8的ram,那么地址需要三位去表示,那么这个函数的方便就体现出来了,你需要使用函数定义就好了,如果对于多文件可以包含定义的文件: 如果你的DEPTH是 ...
- 1286 unknown storage engine innodb
打开my.ini 找到 loose-skip-innodb 与 skip-innodb 前面加上 # 注释掉,重启mysql 服务
- scrapy简单使用
#settings.py文件设置 #如果网站中没有robots文件,就不会抓取任何数据 ROBOTSTXT_OBEY = False #设置请求头 DEFAULT_REQUEST_HEADERS = ...
- 【Luogu4781】【模板】拉格朗日插值
[Luogu4781][模板]拉格朗日插值 题面 洛谷 题解 套个公式就好 #include<cstdio> #define ll long long #define MOD 998244 ...
- emwin 存在多个窗口时,如何获取当前所在窗口
@2019-02-20 [小记] emwin存在多个窗口时,如何获取当前所在窗口 > emwin 之获取当前窗口的一种方法 [需求] 用于在代码中获知当前呈现的是哪个窗口 [方法] 进入新窗口将 ...
- 【原创】tyvj1038 忠诚 & 计蒜客 管家的忠诚 & 线段树(单点更新,区间查询)
最简单的线段树之一,中文题目,不翻译.... 注释讲的比较少,这已经是最简单的线段树,如果看不懂真的说明最基础的理论没明白 推荐一篇文章http://www.cnblogs.com/liwenchi/ ...
- [WC2011]最大XOR和路径(贪心+线性基)
题目大意:给一张无向图,求一条1-n的路径,是路径边权的异或和最小. 题解 这道题的思路很妙,首先我们可以随便找出一条从1到n的路径来,然后我们可以选一些环. 其实不管这个环和这条路径有怎样的关系,我 ...
- SCOI2008着色方案(记忆化搜索)
有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i 种颜色的油漆足够涂ci 个木块.所有油漆刚好足够涂满所有木块,即 c1+c2+...+ck=n.相邻两个木块涂相同色显得很难 ...
- 2019西北工业大学程序设计创新实践基地春季选拔赛(重现赛) Chino with Equation(组合公式)
链接:https://ac.nowcoder.com/acm/contest/553/D来源:牛客网 题目描述 Chino的数学很差,因此Cocoa非常担心.今天,Cocoa要教Chino解不定方程. ...
- poj 3080 Blue Jeans (暴力枚举子串+kmp)
Description The Genographic Project is a research partnership between IBM and The National Geographi ...