业务系统绑定的微信服务号迁移后,用户 openid 的处理与转换
公司有一组业务系统绑定了微信服务号,最近因为运营主体调整,旧的微信服务号要迁移到新的微信服务号上,于是产生了一系列问题。
业务系统中的用户只能使用微信授权登录,所以数据库中存储了用户的 openid。而每一个微信号对于不同的微信服务号,openid 都是不同的,而且服务号迁移后,用户的 openid 会发生变化。
虽然说微信有 unionid 机制(unionid 机制说明:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html),但是由于种种不可抗力的原因,所有的微信服务号都没办法绑定到微信开放平台上,也获取不到 unionid,因此只能另外想办法,使服务号迁移后,业务系统中用户的 openid 也随之换新。
好在微信官方提供了 openid 转换接口,支持服务号建立迁移关系后,将旧 openid 转换为新 openid 的功能。
openid 转换接口说明:https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html
需要注意,转换 openid 接口可在账号迁移审核完成后、双方管理员确认迁移前开始调用,并最多保留15天。若账号迁移没完成,调用时无返回结果或报错。账号迁移15天后,该转换接口将会失效、无法拉取到数据。
附公众号迁移流程:
依据微信提供的 openid 转换接口,写了一个转换新旧 openid 的程序:
@SpringBootTest
class ChangeopenidApplicationTests {
// 业务系统的数据操作Mapper
@Resource
ChangeOpenidMapper changeOpenidMapper;
private static final String newAppId = ""; // 新号appId
private static final String newAppSecret = ""; // 新号appSecret
private static final String newAccessToken = ""; // 新号access_token,通过getNewAccessToken方法获取
private static final String oldAppId = ""; // 旧号appId
/**
* 获取新号access_token
*/
@Test
void getNewAccessToken() {
HttpRequest request = HttpRequest
.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + newAppId
+ "&secret=" + newAppSecret);
String resultStr = request.execute().body();
// 获取到新号access_token后,赋值给newAccessToken,后续转换需要使用
System.out.println("access_token => " + resultStr);
}
/**
* 转换新旧openid
* @param openidList 旧openid列表
*/
JSONArray changeOpenid(String[] openidList) {
HttpRequest request = HttpRequest
.post("https://api.weixin.qq.com/cgi-bin/changeopenid?access_token=" + newAccessToken);
String requestJson = "{\n" +
" \"from_appid\": \"" + oldAppId + "\",\n" +
" \"openid_list\": " + JSONUtil.toJsonStr(openidList) +
"}";
request.body(requestJson);
// 调用openid转换接口批量转换
String resultStr = request.execute().body();
JSONObject resultJson = JSONUtil.parseObj(resultStr);
return resultJson.getJSONArray("result_list");
}
/**
* 批量转换
*/
@Test
void runBatch() {
while (true) {
// 根据业务系统的设计,获取旧Openid列表
List<Map<String, Object>> tomUcenterMemberList = changeOpenidMapper.getTomUcenterMemberList();
List<String> openidList = new ArrayList<>();
for (Map<String, Object> tomUcenterMember : tomUcenterMemberList) {
openidList.add((String) tomUcenterMember.get("openid"));
}
// 开始批量转换
JSONArray resultList = changeOpenid(openidList.toArray(new String[0]));
for (int i = 0; i < resultList.size(); i++) {
// 获取转换结果
String errMsg = resultList.getJSONObject(i).getStr("err_msg");
String oldOpenid = resultList.getJSONObject(i).getStr("ori_openid");
String newOpenid = resultList.getJSONObject(i).getStr("new_openid");
if (!"ok".equals(errMsg)) {
// 转换失败,有可能是微信号注销/封禁等极端情况,保存为-1即可
System.out.println("旧Openid=>" + oldOpenid + "转换错误-1");
changeOpenidMapper.updateTomUcenterMember(oldOpenid, "-1");
} else {
// 将业务系统中所有的旧openid替换为新openid
int result = changeOpenidMapper.updateTomUcenterMember(oldOpenid, newOpenid);
}
}
if (tomUcenterMemberList.isEmpty()) {
break;
}
}
}
/**
* 多线程批量转换
*/
@Test
void runBatchThread() throws InterruptedException {
int threadCount = 30;
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(this::runBatch);
thread.start();
threads.add(thread);
if (i < threadCount - 1) {
Thread.sleep(2000);
}
}
for (Thread thread : threads) {
thread.join();
}
}
}