修复:多个企业微信应用 access_token 并存问题

This commit is contained in:
pppscn 2021-12-01 23:04:04 +08:00
parent f820b4eb94
commit 641c033b1f
8 changed files with 184 additions and 140 deletions

View File

@ -27,10 +27,6 @@ public class MyApplication extends Application {
public static List<PhoneUtils.SimInfo> SimInfoList = new ArrayList<>(); public static List<PhoneUtils.SimInfo> SimInfoList = new ArrayList<>();
//是否关闭页面提示 //是否关闭页面提示
public static boolean showHelpTip = true; public static boolean showHelpTip = true;
//企业微信
public static String QyWxAccessToken;
public static long QyWxAccessTokenExpiresIn = 0;
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {

View File

@ -938,20 +938,23 @@ public class SenderActivity extends AppCompatActivity {
show.dismiss(); show.dismiss();
}); });
buttonQYWXAppTest.setOnClickListener(view -> { buttonQYWXAppTest.setOnClickListener(view -> {
String cropID = editTextQYWXAppCorpID.getText().toString().trim();
String agentID = editTextQYWXAppAgentID.getText().toString().trim(); QYWXAppSettingVo QYWXAppSettingVoNew = new QYWXAppSettingVo(
String secret = editTextQYWXAppSecret.getText().toString().trim(); editTextQYWXAppCorpID.getText().toString().trim(),
String toUser = editTextQYWXAppToUser.getText().toString().trim(); editTextQYWXAppAgentID.getText().toString().trim(),
//Boolean atAll = switchQYWXAppAtAll.isChecked(); editTextQYWXAppSecret.getText().toString().trim(),
if (!toUser.isEmpty()) { editTextQYWXAppToUser.getText().toString().trim(),
try { switchQYWXAppAtAll.isChecked());
SenderQyWxAppMsg.sendMsg(0, handler, cropID, agentID, secret, toUser, R.string.test_content + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())), true); if (QYWXAppSettingVoNew.getToUser().isEmpty()) {
} catch (Exception e) {
Toast.makeText(SenderActivity.this, getString(R.string.failed_to_fwd) + e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
} else {
Toast.makeText(SenderActivity.this, R.string.invalid_at_mobiles, Toast.LENGTH_LONG).show(); Toast.makeText(SenderActivity.this, R.string.invalid_at_mobiles, Toast.LENGTH_LONG).show();
return;
}
try {
SenderQyWxAppMsg.sendMsg(0, handler, senderModel, QYWXAppSettingVoNew, R.string.test_content + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
} catch (Exception e) {
Toast.makeText(SenderActivity.this, getString(R.string.failed_to_fwd) + e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
} }
}); });
} }

View File

@ -11,6 +11,8 @@ public class QYWXAppSettingVo implements Serializable {
private String secret; private String secret;
private String toUser; private String toUser;
private Boolean atAll; private Boolean atAll;
private String accessToken;
private Long expiresIn;
public QYWXAppSettingVo() { public QYWXAppSettingVo() {
} }
@ -23,4 +25,11 @@ public class QYWXAppSettingVo implements Serializable {
this.atAll = atAll; this.atAll = atAll;
} }
public String getAccessToken() {
if (accessToken == null || accessToken.isEmpty() || expiresIn == null || System.currentTimeMillis() > expiresIn) {
return null;
}
return accessToken;
}
} }

View File

@ -193,7 +193,7 @@ public class SendUtil {
QYWXAppSettingVo qYWXAppSettingVo = JSON.parseObject(senderModel.getJsonSetting(), QYWXAppSettingVo.class); QYWXAppSettingVo qYWXAppSettingVo = JSON.parseObject(senderModel.getJsonSetting(), QYWXAppSettingVo.class);
if (qYWXAppSettingVo != null) { if (qYWXAppSettingVo != null) {
try { try {
SenderQyWxAppMsg.sendMsg(logId, handError, qYWXAppSettingVo.getCorpID(), qYWXAppSettingVo.getAgentID(), qYWXAppSettingVo.getSecret(), qYWXAppSettingVo.getToUser(), smsVo.getSmsVoForSend(smsTemplate), false); SenderQyWxAppMsg.sendMsg(logId, handError, senderModel, qYWXAppSettingVo, smsVo.getSmsVoForSend(smsTemplate));
} catch (Exception e) { } catch (Exception e) {
LogUtil.updateLog(logId, 0, e.getMessage()); LogUtil.updateLog(logId, 0, e.getMessage());
Log.e(TAG, "senderSendMsg: qywx_app error " + e.getMessage()); Log.e(TAG, "senderSendMsg: qywx_app error " + e.getMessage());

View File

@ -11,7 +11,8 @@ import androidx.annotation.NonNull;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.idormy.sms.forwarder.MyApplication; import com.idormy.sms.forwarder.model.SenderModel;
import com.idormy.sms.forwarder.model.vo.QYWXAppSettingVo;
import com.idormy.sms.forwarder.utils.LogUtil; import com.idormy.sms.forwarder.utils.LogUtil;
import com.idormy.sms.forwarder.utils.SettingUtil; import com.idormy.sms.forwarder.utils.SettingUtil;
@ -36,127 +37,106 @@ public class SenderQyWxAppMsg extends SenderBaseMsg {
static final String TAG = "SenderQyWxAppMsg"; static final String TAG = "SenderQyWxAppMsg";
public static void sendMsg(final long logId, final Handler handError, String corpID, String agentID, String secret, String toUser, String content, boolean forceRefresh) throws Exception { public static void sendMsg(final long logId, final Handler handError, final SenderModel senderModel, final QYWXAppSettingVo qYWXAppSettingVo, String content) throws Exception {
Log.i(TAG, "sendMsg corpID:" + corpID + " agentID:" + agentID + " secret:" + secret + " toUser:" + toUser + " content:" + content + " forceRefresh:" + forceRefresh);
if (qYWXAppSettingVo == null) {
Toast(handError, TAG, "参数错误");
return;
}
String corpID = qYWXAppSettingVo.getCorpID();
String agentID = qYWXAppSettingVo.getAgentID();
String secret = qYWXAppSettingVo.getSecret();
String toUser = qYWXAppSettingVo.getToUser();
Boolean atAll = qYWXAppSettingVo.getAtAll();
Log.i(TAG, "sendMsg corpID:" + corpID + " agentID:" + agentID + " secret:" + secret + " toUser:" + toUser + " content:" + content);
if (corpID == null || corpID.isEmpty() || agentID == null || agentID.isEmpty() || secret == null || secret.isEmpty()) { if (corpID == null || corpID.isEmpty() || agentID == null || agentID.isEmpty() || secret == null || secret.isEmpty()) {
return; return;
} }
//TODO:判断access_token是否失效
if (forceRefresh
|| MyApplication.QyWxAccessToken == null || MyApplication.QyWxAccessToken.isEmpty()
|| System.currentTimeMillis() > MyApplication.QyWxAccessTokenExpiresIn) {
String getTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?";
getTokenUrl += "corpid=" + corpID;
getTokenUrl += "&corpsecret=" + secret;
Log.d(TAG, "getTokenUrl" + getTokenUrl);
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url(getTokenUrl).get().build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull final IOException e) {
LogUtil.updateLog(logId, 0, e.getMessage());
Log.d(TAG, "onFailure" + e.getMessage());
if (handError != null) {
Message msg = new Message();
msg.what = NOTIFY;
Bundle bundle = new Bundle();
bundle.putString("DATA", "获取access_token失败" + e.getMessage());
msg.setData(bundle);
handError.sendMessage(msg);
}
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
final String json = Objects.requireNonNull(response.body()).string();
Log.d(TAG, "Code" + response.code() + " Response: " + json);
JSONObject jsonObject = JSON.parseObject(json);
int errcode = jsonObject.getInteger("errcode");
if (errcode == 0) {
MyApplication.QyWxAccessToken = jsonObject.getString("access_token");
MyApplication.QyWxAccessTokenExpiresIn = System.currentTimeMillis() + (jsonObject.getInteger("expires_in") - 120) * 1000L; //提前2分钟过期
Log.d(TAG, "access_token" + MyApplication.QyWxAccessToken);
Log.d(TAG, "expires_in" + MyApplication.QyWxAccessTokenExpiresIn);
sendTextMsg(logId, handError, agentID, toUser, content);
} else {
String errmsg = jsonObject.getString("errmsg");
LogUtil.updateLog(logId, 0, errmsg);
Log.d(TAG, "onFailure" + errmsg);
if (handError != null) {
Message msg = new Message();
msg.what = NOTIFY;
Bundle bundle = new Bundle();
bundle.putString("DATA", "获取access_token失败" + errmsg);
msg.setData(bundle);
handError.sendMessage(msg);
}
}
}
});
} else {
sendTextMsg(logId, handError, agentID, toUser, content);
}
}
//发送文本消息
public static void sendTextMsg(final long logId, final Handler handError, String agentID, String toUser, String content) {
Map textMsgMap = new HashMap();
textMsgMap.put("touser", toUser);
textMsgMap.put("msgtype", "text");
textMsgMap.put("agentid", agentID);
Map textText = new HashMap();
textText.put("content", content);
textMsgMap.put("text", textText);
final String requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + MyApplication.QyWxAccessToken;
Log.i(TAG, "requestUrl:" + requestUrl);
final String requestMsg = JSON.toJSONString(textMsgMap);
Log.i(TAG, "requestMsg:" + requestMsg);
Observable Observable
.create((ObservableEmitter<Object> emitter) -> { .create((ObservableEmitter<Object> emitter) -> {
Toast(handError, TAG, "开始请求接口..."); Toast(handError, TAG, "开始请求接口...");
OkHttpClient client = new OkHttpClient(); //TODO:获取有效access_token
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), requestMsg); String accessToken = qYWXAppSettingVo.getAccessToken();
if (accessToken == null || accessToken.isEmpty()) {
final Request request = new Request.Builder() String getTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?";
.url(requestUrl) getTokenUrl += "corpid=" + corpID;
.addHeader("Content-Type", "application/json; charset=utf-8") getTokenUrl += "&corpsecret=" + secret;
.post(requestBody) Log.d(TAG, "getTokenUrl" + getTokenUrl);
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull final IOException e) {
LogUtil.updateLog(logId, 0, e.getMessage());
Toast(handError, TAG, "发送失败:" + e.getMessage());
emitter.onError(new RuntimeException("请求接口异常..."));
}
@Override OkHttpClient client = new OkHttpClient();
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { final Request request = new Request.Builder().url(getTokenUrl).get().build();
final String responseStr = Objects.requireNonNull(response.body()).string(); Call call = client.newCall(request);
Log.d(TAG, "Response" + response.code() + "" + responseStr); call.enqueue(new Callback() {
Toast(handError, TAG, "发送状态:" + responseStr); @Override
public void onFailure(@NonNull Call call, @NonNull final IOException e) {
LogUtil.updateLog(logId, 0, e.getMessage());
qYWXAppSettingVo.setAccessToken("");
qYWXAppSettingVo.setExpiresIn(0L);
if (senderModel != null) {
senderModel.setJsonSetting(JSON.toJSONString(qYWXAppSettingVo));
SenderUtil.updateSender(senderModel);
}
Log.d(TAG, "onFailure" + e.getMessage());
if (handError != null) {
Message msg = new Message();
msg.what = NOTIFY;
Bundle bundle = new Bundle();
bundle.putString("DATA", "获取access_token失败" + e.getMessage());
msg.setData(bundle);
handError.sendMessage(msg);
//TODO:粗略解析是否发送成功 emitter.onError(new RuntimeException("请求接口异常..."));
if (responseStr.contains("\"errcode\":0")) { }
LogUtil.updateLog(logId, 1, responseStr);
} else {
LogUtil.updateLog(logId, 0, responseStr);
} }
}
}); @Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
final String json = Objects.requireNonNull(response.body()).string();
Log.d(TAG, "Code" + response.code() + " Response: " + json);
JSONObject jsonObject = JSON.parseObject(json);
int errcode = jsonObject.getInteger("errcode");
if (errcode == 0) {
String access_token = jsonObject.getString("access_token");
long expires_in = System.currentTimeMillis() + (jsonObject.getInteger("expires_in") - 120) * 1000L; //提前2分钟过期
Log.d(TAG, "access_token" + access_token);
Log.d(TAG, "expires_in" + expires_in);
qYWXAppSettingVo.setAccessToken(access_token);
qYWXAppSettingVo.setExpiresIn(expires_in);
if (senderModel != null) {
senderModel.setJsonSetting(JSON.toJSONString(qYWXAppSettingVo));
SenderUtil.updateSender(senderModel);
}
sendTextMsg(emitter, logId, handError, agentID, toUser, content, access_token);
} else {
String errmsg = jsonObject.getString("errmsg");
LogUtil.updateLog(logId, 0, errmsg);
Log.d(TAG, "onFailure" + errmsg);
if (handError != null) {
Message msg = new Message();
msg.what = NOTIFY;
Bundle bundle = new Bundle();
bundle.putString("DATA", "获取access_token失败" + errmsg);
msg.setData(bundle);
handError.sendMessage(msg);
}
emitter.onError(new RuntimeException("请求接口异常..."));
}
}
});
} else {
sendTextMsg(emitter, logId, handError, agentID, toUser, content, accessToken);
}
}).retryWhen((Observable<Throwable> errorObservable) -> errorObservable }).retryWhen((Observable<Throwable> errorObservable) -> errorObservable
.zipWith(Observable.just( .zipWith(Observable.just(
@ -171,6 +151,59 @@ public class SenderQyWxAppMsg extends SenderBaseMsg {
return Observable.timer(delay, TimeUnit.SECONDS); return Observable.timer(delay, TimeUnit.SECONDS);
})) }))
.subscribe(System.out::println); .subscribe(System.out::println);
}
//发送文本消息
public static void sendTextMsg(ObservableEmitter<Object> emitter, final long logId, final Handler handError, String agentID, String toUser, String content, String accessToken) {
Map textMsgMap = new HashMap();
textMsgMap.put("touser", toUser);
textMsgMap.put("msgtype", "text");
textMsgMap.put("agentid", agentID);
Map textText = new HashMap();
textText.put("content", content);
textMsgMap.put("text", textText);
final String requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
Log.i(TAG, "requestUrl:" + requestUrl);
final String requestMsg = JSON.toJSONString(textMsgMap);
Log.i(TAG, "requestMsg:" + requestMsg);
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), requestMsg);
final Request request = new Request.Builder()
.url(requestUrl)
.addHeader("Content-Type", "application/json; charset=utf-8")
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull final IOException e) {
LogUtil.updateLog(logId, 0, e.getMessage());
Toast(handError, TAG, "发送失败:" + e.getMessage());
emitter.onError(new RuntimeException("请求接口异常..."));
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
final String responseStr = Objects.requireNonNull(response.body()).string();
Log.d(TAG, "Response" + response.code() + "" + responseStr);
Toast(handError, TAG, "发送状态:" + responseStr);
//TODO:粗略解析是否发送成功
if (responseStr.contains("\"errcode\":0")) {
LogUtil.updateLog(logId, 1, responseStr);
} else {
LogUtil.updateLog(logId, 0, responseStr);
}
}
});
} }
} }

View File

@ -17,7 +17,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/set_name" android:text="@string/set_name"
@ -42,7 +42,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/Corp_ID" android:text="@string/Corp_ID"
@ -67,7 +67,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/Agent_ID" android:text="@string/Agent_ID"
@ -93,7 +93,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/App_Secret" android:text="@string/App_Secret"
@ -118,7 +118,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/is_at_all" android:text="@string/is_at_all"
@ -128,7 +128,7 @@
android:id="@+id/switchQYWXAppAtAll" android:id="@+id/switchQYWXAppAtAll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="false" android:checked="true"
tools:ignore="UseSwitchCompatOrMaterialXml" /> tools:ignore="UseSwitchCompatOrMaterialXml" />
</LinearLayout> </LinearLayout>
@ -136,7 +136,8 @@
android:id="@+id/linearLayoutQYWXAppToUser" android:id="@+id/linearLayoutQYWXAppToUser"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical"
android:visibility="gone">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -146,7 +147,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:layout_width="60dp" android:layout_width="80dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="@string/specified_member" android:text="@string/specified_member"
@ -159,7 +160,7 @@
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:autofillHints="" android:autofillHints=""
android:inputType="text" android:inputType="text"
android:text="" android:text="@string/at_all"
tools:ignore="LabelFor" /> tools:ignore="LabelFor" />
</LinearLayout> </LinearLayout>

View File

@ -140,6 +140,7 @@
<string name="App_Secret">App Secret</string> <string name="App_Secret">App Secret</string>
<string name="is_at_all">Is at all</string> <string name="is_at_all">Is at all</string>
<string name="specified_member">Specified Member</string> <string name="specified_member">Specified Member</string>
<string name="at_all"><![CDATA[@all]]></string>
<string name="specified_member_tips">TipSpecify members receive messages, member ID list (multiple recipients with \'|\' space, maximum 1000)</string> <string name="specified_member_tips">TipSpecify members receive messages, member ID list (multiple recipients with \'|\' space, maximum 1000)</string>
<string name="QYWXGroupRobotWebHook">WebHook, e.g. https://qyapi.weixin.qq.com/cgixx?key=xxx</string> <string name="QYWXGroupRobotWebHook">WebHook, e.g. https://qyapi.weixin.qq.com/cgixx?key=xxx</string>
<string name="ServerChanSendKey">ServerChan\'s SendKey</string> <string name="ServerChanSendKey">ServerChan\'s SendKey</string>

View File

@ -135,11 +135,12 @@
<string name="email_title">邮件主题</string> <string name="email_title">邮件主题</string>
<string name="feishu_webhook">Webhook 地址</string> <string name="feishu_webhook">Webhook 地址</string>
<string name="feishu_secret">加签 Secret (没有可不填)</string> <string name="feishu_secret">加签 Secret (没有可不填)</string>
<string name="Corp_ID">Corp ID</string> <string name="Corp_ID">企业ID</string>
<string name="Agent_ID">Agent ID</string> <string name="Agent_ID">应用AgentId</string>
<string name="App_Secret">App Secret</string> <string name="App_Secret">应用Secret</string>
<string name="is_at_all">是否@所有人</string> <string name="is_at_all">是否@all</string>
<string name="specified_member">指定成员</string> <string name="specified_member">指定成员</string>
<string name="at_all"><![CDATA[@all]]></string>
<string name="specified_member_tips">Tip指定接收消息的成员成员ID列表多个接收者用|分隔最多支持1000个</string> <string name="specified_member_tips">Tip指定接收消息的成员成员ID列表多个接收者用|分隔最多支持1000个</string>
<string name="QYWXGroupRobotWebHook">设置WebHook地址:示例https://qyapi.weixin.qq.com/cgixx?key=xxx</string> <string name="QYWXGroupRobotWebHook">设置WebHook地址:示例https://qyapi.weixin.qq.com/cgixx?key=xxx</string>
<string name="ServerChanSendKey">设置Server酱·Turbo版的SendKey</string> <string name="ServerChanSendKey">设置Server酱·Turbo版的SendKey</string>