diff --git a/winboll/build.gradle b/winboll/build.gradle
index 548cc8b..774d29e 100644
--- a/winboll/build.gradle
+++ b/winboll/build.gradle
@@ -43,6 +43,12 @@ android {
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
+
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs'] // 若SO库放在libs目录下
+ }
+ }
}
dependencies {
@@ -63,6 +69,10 @@ dependencies {
api 'io.github.medyo:android-about-page:2.0.0'
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
+ // OkHttp网络请求
+ implementation 'com.squareup.okhttp3:okhttp:3.14.9'
+ // FastJSON解析
+ implementation 'com.alibaba:fastjson:1.2.76'
// AndroidX 类库
api 'androidx.appcompat:appcompat:1.1.0'
diff --git a/winboll/build.properties b/winboll/build.properties
index 0bd08a6..dc0bb79 100644
--- a/winboll/build.properties
+++ b/winboll/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Wed Dec 31 20:28:06 HKT 2025
+#Sun Jan 04 09:03:20 GMT 2026
stageCount=9
libraryProject=
baseVersion=15.11
publishVersion=15.11.8
-buildCount=0
+buildCount=5
baseBetaVersion=15.11.9
diff --git a/winboll/libs/libWeWorkSpecSDK.so b/winboll/libs/libWeWorkSpecSDK.so
new file mode 100644
index 0000000..d9f592b
Binary files /dev/null and b/winboll/libs/libWeWorkSpecSDK.so differ
diff --git a/winboll/src/main/AndroidManifest.xml b/winboll/src/main/AndroidManifest.xml
index cfcf3fb..8236f00 100644
--- a/winboll/src/main/AndroidManifest.xml
+++ b/winboll/src/main/AndroidManifest.xml
@@ -278,6 +278,10 @@
+
+
+
+
-
+
\ No newline at end of file
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java
index 24f4d2a..935b86f 100644
--- a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java
@@ -1,6 +1,6 @@
package cc.winboll.studio.winboll;
-import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -8,20 +8,17 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
+import cc.winboll.studio.libaes.activitys.AboutActivity;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
-import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.APPInfo;
import cc.winboll.studio.libaes.models.DrawerMenuBean;
-import cc.winboll.studio.libaes.unittests.TestAButtonFragment;
-import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.activities.SettingsActivity;
+import cc.winboll.studio.winboll.activities.WXPayActivity;
import cc.winboll.studio.winboll.fragments.BrowserFragment;
import java.util.ArrayList;
-import android.content.Intent;
-import cc.winboll.studio.libaes.activitys.AboutActivity;
public class MainActivity extends DrawerFragmentActivity {
@@ -152,6 +149,8 @@ public class MainActivity extends DrawerFragmentActivity {
}
} else if (nItemId == R.id.item_settings) {
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), SettingsActivity.class);
+ } else if (nItemId == R.id.item_wxpayactivity) {
+ WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), WXPayActivity.class);
} else if (nItemId == cc.winboll.studio.libaes.R.id.item_about) {
Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
APPInfo appInfo = genDefaultAPPInfo();
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java b/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java
new file mode 100644
index 0000000..69c7493
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java
@@ -0,0 +1,28 @@
+package cc.winboll.studio.winboll;
+
+/**
+ * 微信支付配置类
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class WxPayConfig {
+ // ========== 核心修改点:替换为你的服务端地址 ==========
+ // 服务端IP/域名 + 端口(Docker部署的服务端,需确保安卓端可访问)
+ public static final String BASE_URL = "https://wxpay.winboll.cc";
+
+ // 统一下单接口路径(对应服务端的测试接口)
+ public static final String CREATE_ORDER_URL = BASE_URL + "/pay/createOrder";
+
+ // 订单查询接口路径
+ public static final String QUERY_ORDER_URL = BASE_URL + "/pay/queryOrder";
+
+ // ========== 固定支付配置 ==========
+ public static final String ORDER_BODY = "定额测试支付"; // 商品描述
+ public static final int TOTAL_FEE = 1; // 固定金额:1分(沙箱环境推荐)
+ public static final String TRADE_TYPE = "NATIVE"; // 支付类型:二维码
+
+ // ========== 轮询配置 ==========
+ public static final long POLL_INTERVAL = 10000; // 轮询间隔:10秒
+ public static final long POLL_TIMEOUT = 45000; // 轮询超时:45秒
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java
new file mode 100644
index 0000000..e881993
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java
@@ -0,0 +1,211 @@
+package cc.winboll.studio.winboll.activities;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
+import cc.winboll.studio.winboll.R;
+import cc.winboll.studio.winboll.WxPayConfig;
+import cc.winboll.studio.winboll.utils.SpecUtil;
+import cc.winboll.studio.winboll.utils.WxPayApi;
+import cc.winboll.studio.winboll.utils.ZXingUtils;
+import java.util.Timer;
+import java.util.TimerTask;
+
+
+
+/**
+ * 主界面:生成二维码 + 轮询查询支付结果
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class WXPayActivity extends WinBoLLActivity implements IWinBoLLActivity {
+
+ private static final String TAG = "WXPayActivity";
+
+ // Handler消息标识
+ private static final int MSG_POLL_TIMEOUT = 1001;
+ private static final int MSG_POLL_SUCCESS = 1002;
+ private static final int MSG_POLL_FAILED = 1003;
+
+ private ImageView mIvQrCode;
+ private TextView mTvOrderNo;
+ private TextView mTvPayStatus;
+ private Button mBtnCreateOrder;
+
+ private String mOutTradeNo; // 商户订单号
+ private Timer mPollTimer; // 轮询定时器
+ private long mPollStartTime; // 轮询开始时间
+
+
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ private Handler mPollHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch (msg.what) {
+ case MSG_POLL_TIMEOUT:
+ stopPoll();
+ mTvPayStatus.setText("支付状态:轮询超时");
+ mTvPayStatus.setTextColor(getResources().getColor(android.R.color.darker_gray));
+ Toast.makeText(WXPayActivity.this, "轮询超时,请手动查询", Toast.LENGTH_SHORT).show();
+ break;
+ case MSG_POLL_SUCCESS:
+ boolean isPaySuccess = (boolean) msg.obj;
+ String tradeState = (String) msg.getData().getString("tradeState");
+ stopPoll();
+ if (isPaySuccess) {
+ mTvPayStatus.setText("支付状态:支付成功 ✅");
+ mTvPayStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
+ Toast.makeText(WXPayActivity.this, "支付成功!", Toast.LENGTH_SHORT).show();
+ } else {
+ mTvPayStatus.setText("支付状态:" + tradeState);
+ mTvPayStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
+ }
+ break;
+ case MSG_POLL_FAILED:
+ String errorMsg = (String) msg.obj;
+ mTvPayStatus.setText("查询失败:" + errorMsg);
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_wxpay);
+
+ initView();
+ initListener();
+ }
+
+ private void initView() {
+ mIvQrCode = findViewById(R.id.iv_qrcode);
+ mTvOrderNo = findViewById(R.id.tv_order_no);
+ mTvPayStatus = findViewById(R.id.tv_pay_status);
+ mBtnCreateOrder = findViewById(R.id.btn_create_order);
+ }
+
+ private void initListener() {
+ mBtnCreateOrder.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createOrder();
+ }
+ });
+ }
+
+ /**
+ * 统一下单,生成二维码
+ */
+ private void createOrder() {
+ mBtnCreateOrder.setEnabled(false);
+ mTvPayStatus.setText("支付状态:生成订单中...");
+ mIvQrCode.setImageBitmap(null);
+
+ WxPayApi.createOrder(new WxPayApi.OnCreateOrderCallback() {
+ @Override
+ public void onSuccess(String outTradeNo, String codeUrl) {
+ mOutTradeNo = outTradeNo;
+ mTvOrderNo.setText("商户订单号:" + outTradeNo);
+ mTvPayStatus.setText("支付状态:未支付,请扫码");
+
+ // 生成二维码
+ Bitmap qrCodeBitmap = ZXingUtils.createQRCodeBitmap(codeUrl, 250, 250);
+ if (qrCodeBitmap != null) {
+ mIvQrCode.setImageBitmap(qrCodeBitmap);
+ // 开始轮询查询支付结果
+ startPoll();
+ } else {
+ mTvPayStatus.setText("支付状态:生成二维码失败");
+ mBtnCreateOrder.setEnabled(true);
+ }
+ }
+
+ @Override
+ public void onFailure(String errorMsg) {
+ SpecUtil.WWSpecLogError(TAG, "统一下单失败:" + errorMsg);
+ mTvPayStatus.setText("生成订单失败:" + errorMsg);
+ mBtnCreateOrder.setEnabled(true);
+ Toast.makeText(WXPayActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ /**
+ * 开始轮询查询支付结果
+ */
+ private void startPoll() {
+ stopPoll(); // 先停止之前的轮询
+ mPollStartTime = System.currentTimeMillis();
+ mPollTimer = new Timer();
+ mPollTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ // 检查是否超时
+ long elapsedTime = System.currentTimeMillis() - mPollStartTime;
+ if (elapsedTime >= WxPayConfig.POLL_TIMEOUT) {
+ mPollHandler.sendEmptyMessage(MSG_POLL_TIMEOUT);
+ return;
+ }
+
+ // 查询订单状态
+ WxPayApi.queryOrder(mOutTradeNo, new WxPayApi.OnQueryOrderCallback() {
+ @Override
+ public void onSuccess(boolean isPaySuccess, String tradeState) {
+ Message msg = Message.obtain();
+ msg.what = MSG_POLL_SUCCESS;
+ msg.obj = isPaySuccess;
+ Bundle bundle = new Bundle();
+ bundle.putString("tradeState", tradeState);
+ msg.setData(bundle);
+ mPollHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onFailure(String errorMsg) {
+ Message msg = Message.obtain();
+ msg.what = MSG_POLL_FAILED;
+ msg.obj = errorMsg;
+ mPollHandler.sendMessage(msg);
+ }
+ });
+ }
+ }, 0, WxPayConfig.POLL_INTERVAL);
+ }
+
+ /**
+ * 停止轮询
+ */
+ private void stopPoll() {
+ if (mPollTimer != null) {
+ mPollTimer.cancel();
+ mPollTimer = null;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ stopPoll();
+ }
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java b/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java
new file mode 100644
index 0000000..5fdac27
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java
@@ -0,0 +1,311 @@
+package cc.winboll.studio.winboll.unittest;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.winboll.R;
+import cc.winboll.studio.winboll.activities.WinBoLLActivity;
+
+/**
+ * @Author 豆包&ZhanGSKen
+ * @Date 2026/01/03 10:52
+ * @Describe 企业微信SDK接口测试(基础调试版)
+ * 包含:SDK初始化、基础接口调用、日志输出、主线程回调处理
+ */
+public class TestWeWorkSpecSDK extends WinBoLLActivity implements IWinBoLLActivity, View.OnClickListener {
+
+ public static final String TAG = "TestWeWorkSpecSDK";
+
+ // ------------------- 企业微信SDK配置常量(需替换为实际项目参数) -------------------
+ // 企业微信 CorpID(从企业微信管理后台获取)
+ private static final String CORP_ID = "wwb37c73f34c722852";
+ // 应用 AgentID(从企业微信应用管理后台获取)
+ private static final String AGENT_ID = "your_agent_id_here";
+ // 应用 Secret(从企业微信应用管理后台获取,注意保密)
+ private static final String APP_SECRET = "your_app_secret_here";
+
+ // ------------------- Handler消息标识(主线程处理SDK回调) -------------------
+ private static final int MSG_SDK_INIT_SUCCESS = 1001;
+ private static final int MSG_SDK_INIT_FAILED = 1002;
+ private static final int MSG_GET_CORP_INFO_SUCCESS = 1003;
+ private static final int MSG_GET_CORP_INFO_FAILED = 1004;
+
+ // ------------------- 控件声明 -------------------
+ private Button mBtnInitSDK;
+ private Button mBtnGetCorpInfo;
+ private Button mBtnCheckAuth;
+
+ // ------------------- 主线程Handler(处理SDK异步回调) -------------------
+ private Handler mWeWorkHandler;
+
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_test_weworkspecsdk);
+
+ // 初始化控件
+ initViews();
+ // 绑定点击事件
+ initEvents();
+ // 初始化Handler(主线程处理回调,更新UI)
+ initHandler();
+ // 初始化SDK(可选:启动时自动初始化,或点击按钮初始化)
+ // initWeWorkSDK();
+ }
+
+ /**
+ * 初始化控件(Java 7 显式绑定)
+ */
+ private void initViews() {
+ mBtnInitSDK = (Button) findViewById(R.id.btn_init_sdk);
+ mBtnGetCorpInfo = (Button) findViewById(R.id.btn_get_corp_info);
+ mBtnCheckAuth = (Button) findViewById(R.id.btn_check_auth);
+ }
+
+ /**
+ * 绑定点击事件(Java 7 匿名内部类)
+ */
+ private void initEvents() {
+ mBtnInitSDK.setOnClickListener(this);
+ mBtnGetCorpInfo.setOnClickListener(this);
+ mBtnCheckAuth.setOnClickListener(this);
+ }
+
+ /**
+ * 初始化主线程Handler(处理SDK异步回调,安全更新UI)
+ */
+ private void initHandler() {
+ mWeWorkHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch (msg.what) {
+ case MSG_SDK_INIT_SUCCESS:
+ showToast("企业微信SDK初始化成功");
+ LogUtils.d(TAG, "SDK初始化成功");
+ break;
+ case MSG_SDK_INIT_FAILED:
+ String initError = (String) msg.obj;
+ showToast("SDK初始化失败:" + initError);
+ LogUtils.e(TAG, "SDK初始化失败:" + initError);
+ break;
+ case MSG_GET_CORP_INFO_SUCCESS:
+ String corpInfo = (String) msg.obj;
+ showToast("获取企业信息成功");
+ LogUtils.d(TAG, "企业信息:" + corpInfo);
+ break;
+ case MSG_GET_CORP_INFO_FAILED:
+ String corpError = (String) msg.obj;
+ showToast("获取企业信息失败:" + corpError);
+ LogUtils.e(TAG, "获取企业信息失败:" + corpError);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ }
+
+ // ------------------- 企业微信SDK核心接口调用 -------------------
+
+ /**
+ * 初始化企业微信SDK(异步操作,通过Handler回调结果)
+ */
+ private void initWeWorkSDK() {
+ showToast("开始初始化企业微信SDK...");
+ // 模拟SDK异步初始化(实际项目中替换为企业微信SDK的真实初始化接口)
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // 真实SDK初始化逻辑示例:
+ // WeWorkSDK.init(TestWeWorkSpecSDK.this, CORP_ID, AGENT_ID, new WeWorkSDKCallback() {
+ // @Override
+ // public void onSuccess() {
+ // mWeWorkHandler.sendEmptyMessage(MSG_SDK_INIT_SUCCESS);
+ // }
+ //
+ // @Override
+ // public void onFailure(String errorMsg) {
+ // Message msg = Message.obtain();
+ // msg.what = MSG_SDK_INIT_FAILED;
+ // msg.obj = errorMsg;
+ // mWeWorkHandler.sendMessage(msg);
+ // }
+ // });
+
+ // 调试模拟:休眠1秒,模拟异步初始化
+ Thread.sleep(1000);
+ // 模拟初始化成功(如需测试失败,替换为发送MSG_SDK_INIT_FAILED)
+ mWeWorkHandler.sendEmptyMessage(MSG_SDK_INIT_SUCCESS);
+ // 模拟初始化失败
+ // Message msg = Message.obtain();
+ // msg.what = MSG_SDK_INIT_FAILED;
+ // msg.obj = "CorpID或AgentID错误";
+ // mWeWorkHandler.sendMessage(msg);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ Message msg = Message.obtain();
+ msg.what = MSG_SDK_INIT_FAILED;
+ msg.obj = "线程中断:" + e.getMessage();
+ mWeWorkHandler.sendMessage(msg);
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * 获取企业基本信息(异步操作,需先初始化SDK)
+ */
+ private void getCorpInfo() {
+ if (!isSDKInitialized()) {
+ showToast("请先初始化SDK");
+ return;
+ }
+ showToast("开始获取企业信息...");
+ // 模拟SDK异步获取企业信息(实际项目中替换为真实接口)
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // 真实SDK接口示例:
+ // WeWorkSDK.getCorpInfo(APP_SECRET, new CorpInfoCallback() {
+ // @Override
+ // public void onSuccess(CorpInfo info) {
+ // Message msg = Message.obtain();
+ // msg.what = MSG_GET_CORP_INFO_SUCCESS;
+ // msg.obj = "企业名称:" + info.getCorpName() + ",企业ID:" + info.getCorpId();
+ // mWeWorkHandler.sendMessage(msg);
+ // }
+ //
+ // @Override
+ // public void onFailure(String errorMsg) {
+ // Message msg = Message.obtain();
+ // msg.what = MSG_GET_CORP_INFO_FAILED;
+ // msg.obj = errorMsg;
+ // mWeWorkHandler.sendMessage(msg);
+ // }
+ // });
+
+ // 调试模拟:休眠1秒,模拟异步获取
+ Thread.sleep(1000);
+ // 模拟获取成功
+ Message successMsg = Message.obtain();
+ successMsg.what = MSG_GET_CORP_INFO_SUCCESS;
+ successMsg.obj = "企业名称:WinBoLL Studio,企业ID:" + CORP_ID;
+ mWeWorkHandler.sendMessage(successMsg);
+ // 模拟获取失败
+ // Message failMsg = Message.obtain();
+ // failMsg.what = MSG_GET_CORP_INFO_FAILED;
+ // failMsg.obj = "AppSecret错误或权限不足";
+ // mWeWorkHandler.sendMessage(failMsg);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ Message msg = Message.obtain();
+ msg.what = MSG_GET_CORP_INFO_FAILED;
+ msg.obj = "线程中断:" + e.getMessage();
+ mWeWorkHandler.sendMessage(msg);
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * 检查当前用户是否已授权(同步操作,示例)
+ */
+ private void checkAuthStatus() {
+ if (!isSDKInitialized()) {
+ showToast("请先初始化SDK");
+ return;
+ }
+ // 真实SDK接口示例:
+ // boolean isAuthorized = WeWorkSDK.isAuthorized();
+ // 调试模拟:默认返回true
+ boolean isAuthorized = true;
+
+ if (isAuthorized) {
+ showToast("用户已授权");
+ LogUtils.d(TAG, "当前用户已授权企业微信应用");
+ } else {
+ showToast("用户未授权,请先授权");
+ LogUtils.d(TAG, "当前用户未授权企业微信应用");
+ // 真实项目中可调用授权接口:
+ // WeWorkSDK.requestAuth(TestWeWorkSpecSDK.this, new AuthCallback() {
+ // @Override
+ // public void onSuccess(String code) {
+ // showToast("授权成功,code:" + code);
+ // }
+ //
+ // @Override
+ // public void onFailure(String errorMsg) {
+ // showToast("授权失败:" + errorMsg);
+ // }
+ // });
+ }
+ }
+
+ // ------------------- 工具方法 -------------------
+
+ /**
+ * 检查SDK是否已初始化(模拟方法,实际项目中替换为SDK的真实状态检查)
+ */
+ private boolean isSDKInitialized() {
+ // 真实SDK可通过静态方法检查状态:
+ // return WeWorkSDK.isInitialized();
+ // 调试模拟:假设Handler不为空即表示已初始化
+ return mWeWorkHandler != null;
+ }
+
+ /**
+ * 显示Toast提示(Java 7 简化封装)
+ */
+ private void showToast(String msg) {
+ Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+ }
+
+ // ------------------- 点击事件处理 -------------------
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btn_init_sdk) {
+ initWeWorkSDK();
+ } else if (id == R.id.btn_get_corp_info) {
+ getCorpInfo();
+ } else if (id == R.id.btn_check_auth) {
+ checkAuthStatus();
+ }
+ }
+
+ // ------------------- 生命周期管理 -------------------
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // 释放Handler资源,避免内存泄漏
+ if (mWeWorkHandler != null) {
+ mWeWorkHandler.removeCallbacksAndMessages(null);
+ mWeWorkHandler = null;
+ }
+ // 真实SDK需调用销毁方法:
+ // WeWorkSDK.destroy();
+ }
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java
new file mode 100644
index 0000000..4e23dcb
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java
@@ -0,0 +1,83 @@
+package cc.winboll.studio.winboll.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * OkHttp网络请求工具类
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class OkHttpUtil {
+
+ private static OkHttpClient sOkHttpClient;
+ private static Handler sMainHandler = new Handler(Looper.getMainLooper());
+
+ static {
+ sOkHttpClient = new OkHttpClient.Builder()
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .readTimeout(10, TimeUnit.SECONDS)
+ .writeTimeout(10, TimeUnit.SECONDS)
+ .build();
+ }
+
+ /**
+ * GET请求
+ * @param url 请求地址
+ * @param callback 回调
+ */
+ public static void get(String url, final OnResultCallback callback) {
+ Request request = new Request.Builder()
+ .url(url)
+ .get()
+ .build();
+
+ sOkHttpClient.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, final IOException e) {
+ sMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (callback != null) {
+ callback.onFailure(e.getMessage());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Call call, final Response response) throws IOException {
+ final String result = response.body().string();
+ sMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ callback.onSuccess(result);
+ } else {
+ callback.onFailure("请求失败:" + response.code());
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * 回调接口
+ */
+ public interface OnResultCallback {
+ void onSuccess(String result);
+ void onFailure(String errorMsg);
+ }
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java
new file mode 100644
index 0000000..279f286
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java
@@ -0,0 +1,26 @@
+package cc.winboll.studio.winboll.utils;
+
+import cc.winboll.studio.libappbase.LogUtils;
+
+/**
+ * 日志工具类(适配项目规范)
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class SpecUtil {
+
+ private static final boolean isDebug = true;
+
+ public static void WWSpecLogInfo(String tag, String msg) {
+ if (isDebug) {
+ LogUtils.i(tag, msg);
+ }
+ }
+
+ public static void WWSpecLogError(String tag, String msg) {
+ if (isDebug) {
+ LogUtils.e(tag, msg);
+ }
+ }
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java
new file mode 100644
index 0000000..dd0b508
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java
@@ -0,0 +1,100 @@
+package cc.winboll.studio.winboll.utils;
+
+import cc.winboll.studio.winboll.WxPayConfig;
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * 微信支付服务端接口封装
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class WxPayApi {
+
+ /**
+ * 统一下单(生成二维码)
+ * @param callback 回调
+ */
+ public static void createOrder(final OnCreateOrderCallback callback) {
+ // 拼接请求参数(服务端测试接口需支持GET传参,若为POST需修改为表单/JSON)
+ String url = WxPayConfig.CREATE_ORDER_URL +
+ "?body=" + WxPayConfig.ORDER_BODY +
+ "&totalFee=" + WxPayConfig.TOTAL_FEE +
+ "&tradeType=" + WxPayConfig.TRADE_TYPE;
+
+ OkHttpUtil.get(url, new OkHttpUtil.OnResultCallback() {
+ @Override
+ public void onSuccess(String result) {
+ try {
+ JSONObject jsonObject = JSONObject.parseObject(result);
+ String outTradeNo = jsonObject.getString("out_trade_no");
+ String codeUrl = jsonObject.getString("code_url");
+ if (callback != null) {
+ callback.onSuccess(outTradeNo, codeUrl);
+ }
+ } catch (Exception e) {
+ if (callback != null) {
+ callback.onFailure("解析统一下单结果失败:" + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(String errorMsg) {
+ if (callback != null) {
+ callback.onFailure("统一下单请求失败:" + errorMsg);
+ }
+ }
+ });
+ }
+
+ /**
+ * 订单查询
+ * @param outTradeNo 商户订单号
+ * @param callback 回调
+ */
+ public static void queryOrder(String outTradeNo, final OnQueryOrderCallback callback) {
+ String url = WxPayConfig.QUERY_ORDER_URL + "?outTradeNo=" + outTradeNo;
+
+ OkHttpUtil.get(url, new OkHttpUtil.OnResultCallback() {
+ @Override
+ public void onSuccess(String result) {
+ try {
+ JSONObject jsonObject = JSONObject.parseObject(result);
+ String tradeState = jsonObject.getString("trade_state");
+ boolean isSuccess = "SUCCESS".equals(tradeState);
+ if (callback != null) {
+ callback.onSuccess(isSuccess, tradeState);
+ }
+ } catch (Exception e) {
+ if (callback != null) {
+ callback.onFailure("解析订单查询结果失败:" + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(String errorMsg) {
+ if (callback != null) {
+ callback.onFailure("订单查询请求失败:" + errorMsg);
+ }
+ }
+ });
+ }
+
+ /**
+ * 统一下单回调接口
+ */
+ public interface OnCreateOrderCallback {
+ void onSuccess(String outTradeNo, String codeUrl);
+ void onFailure(String errorMsg);
+ }
+
+ /**
+ * 订单查询回调接口
+ */
+ public interface OnQueryOrderCallback {
+ void onSuccess(boolean isPaySuccess, String tradeState);
+ void onFailure(String errorMsg);
+ }
+}
+
diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java
new file mode 100644
index 0000000..389298d
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java
@@ -0,0 +1,75 @@
+package cc.winboll.studio.winboll.utils;
+
+import android.graphics.Bitmap;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.journeyapps.barcodescanner.BarcodeEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ZXing二维码生成工具类
+ * 依赖:com.google.zxing:core:3.4.1 + com.journeyapps:zxing-android-embedded:3.6.0
+ * @Author ZhanGSKen
+ * @Date 2026/01/07
+ */
+public class ZXingUtils {
+
+ /**
+ * 生成二维码Bitmap(核心方法,使用journeyapps工具类)
+ * @param content 内容(如微信支付的code_url)
+ * @param width 二维码宽度(px)
+ * @param height 二维码高度(px)
+ * @return 二维码Bitmap,失败返回null
+ */
+ public static Bitmap createQRCodeBitmap(String content, int width, int height) {
+ // 1. 入参合法性校验
+ if (content == null || content.trim().isEmpty()) {
+ return null;
+ }
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+
+ // 2. 配置二维码参数
+ Map hints = new HashMap<>();
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 字符编码
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 高容错级别(H级可容忍30%遮挡)
+ hints.put(EncodeHintType.MARGIN, 1); // 边距(值越小,二维码越紧凑,建议1-4)
+
+ try {
+ // 3. 生成BitMatrix(二维码矩阵)
+ QRCodeWriter qrCodeWriter = new QRCodeWriter();
+ BitMatrix bitMatrix = qrCodeWriter.encode(
+ content,
+ BarcodeFormat.QR_CODE,
+ width,
+ height,
+ hints
+ );
+
+ // 4. 转换BitMatrix为Bitmap(关键:使用journeyapps的BarcodeEncoder)
+ BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
+ return barcodeEncoder.createBitmap(bitMatrix);
+
+ } catch (WriterException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 重载方法:生成正方形二维码(宽度=高度)
+ * @param content 内容
+ * @param size 二维码边长(px)
+ * @return 二维码Bitmap
+ */
+ public static Bitmap createQRCodeBitmap(String content, int size) {
+ return createQRCodeBitmap(content, size, size);
+ }
+}
+
diff --git a/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java b/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java
new file mode 100644
index 0000000..18faee5
--- /dev/null
+++ b/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java
@@ -0,0 +1,210 @@
+package com.tencent.wework;
+
+import java.util.Map;
+import java.util.HashMap;;
+
+/**
+ * @warning: 1. 不要修改成员变量名,native方法内有反射调用
+ * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内
+ * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件
+ */
+public final class SpecCallbackSDK {
+
+ /**
+ * @description 调用本地方法后实例化的对象指针
+ */
+ private long specCallbackSDKptr = 0;
+
+ public long GetPtr() { return specCallbackSDKptr; }
+
+ /**
+ * @description: 回包的headers
+ */
+ private Map responseHeaders;
+
+ public Map GetResponseHeaders() { return responseHeaders; }
+
+ /**
+ * @description: 回包的加密后的body
+ */
+ private String responseBody;
+
+ public String GetResponseBody() { return responseBody; }
+
+ /**
+ * @description: 每个请求构造一个SpecCallbackSDK示例,
+ * SpecCallbackSDK仅持有headers和body的引用,
+ * 因此需保证headers和body的生存期比SpecCallbackSDK长
+ * @param method: 请求方法GET/POST
+ * @param headers: 请求header
+ * @param body: 请求body
+ * @example:
+ * SpecCallbackSDK sdk = new SpecCallbackSDK(method, headers, body);
+ * if (sdk.IsOk()) {
+ * String corpid = sdk.GetCorpId();
+ * String agentid = sdk.GetAgentId();
+ * String call_type = sdk.GetCallType();
+ * String data = sdk.GetData();
+ * //do something...
+ * }
+ * String response = ...;
+ * sdk.BuildResponseHeaderBody(response);
+ * Map responseHeaders = sdk.GetResponseHeaders();
+ * String body = sdk.GetResponseBody();
+ * //do response
+ *
+ * @return errorcode 示例如下:
+ * -920001: 未设置请求方法
+ * -920002: 未设置请求header
+ * -920003: 未设置请求body
+ * */
+ public SpecCallbackSDK(String method, Map headers, String body) {
+ try {
+ specCallbackSDKptr = NewCallbackSDK(method, headers, body);
+ } catch (Exception e) {
+ SpecUtil.WWSpecLogError("SpecCallbackSDK exception caught", e.getMessage());
+ }
+ }
+
+ private native long NewCallbackSDK(String method, Map headers, String body);
+
+ /**
+ * @usage 在Java对象的内存回收前析构C++对象
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ DeleteCPPInstance(specCallbackSDKptr);
+ super.finalize();
+ }
+
+ private native void DeleteCPPInstance(long specCallbackSDKptr);
+
+ /**
+ * @description: 判断构造函数中传入的请求是否解析成功
+ * @return: 成功与否
+ * */
+ public boolean IsOk() {
+ return IsOk(specCallbackSDKptr);
+ }
+
+ private native boolean IsOk(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的企业
+ * @require: 仅当IsOk() == true可调用
+ * @return: corpid
+ * */
+ public String GetCorpId() {
+ return GetCorpId(specCallbackSDKptr);
+ }
+
+ private native String GetCorpId(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的应用
+ * @require: 仅当IsOk() == true可调用
+ * @return: agentid
+ * */
+ public long GetAgentId() {
+ return GetAgentId(specCallbackSDKptr);
+ }
+
+ private native long GetAgentId(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的类型
+ * @require: 仅当IsOk() == true可调用
+ * @return: 1 - 来自[应用调用专区]的请求
+ * 2 - 来自企业微信的回调事件
+ * */
+ public long GetCallType() {
+ return GetCallType(specCallbackSDKptr);
+ }
+
+ private native long GetCallType(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求数据
+ * @require: 仅当IsOk() == true可调用
+ * @return: 请求数据,根据call_type可能是:
+ * - 企业微信回调事件
+ * - [应用调用专区]接口中的request_data
+ * */
+ public String GetData() {
+ return GetData(specCallbackSDKptr);
+ }
+
+ private native String GetData(long specCallbackSDKptr);
+
+ /**
+ * @description: 是否异步请求
+ * @require: 仅当IsOk() == true可调用
+ * @return: 是否异步请求
+ * */
+ public boolean GetIsAsync() {
+ return GetIsAsync(specCallbackSDKptr);
+ }
+
+ private native boolean GetIsAsync(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的job_info,
+ * @require: 仅当IsOk() == true可调用
+ * @return: job_info,无需理解内容,
+ * 在同一个请求上下文中使用SpecSDK的时候传入
+ * */
+ public String GetJobInfo() {
+ return GetJobInfo(specCallbackSDKptr);
+ }
+
+ private native String GetJobInfo(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的ability_id,[应用调用专区]接口时指定
+ * @require: 仅当IsOk() == true可调用
+ * @return: ability_id
+ * */
+ public String GetAbilityId() {
+ return GetAbilityId(specCallbackSDKptr);
+ }
+
+ private native String GetAbilityId(long specCallbackSDKptr);
+
+ /**
+ * @description: 获取请求的notify_id,用于[应用同步调用专区程序]接口
+ * @require: 仅当IsOk() == true可调用
+ * @return: notify_id
+ * */
+ public String GetNotifyId() {
+ return GetNotifyId(specCallbackSDKptr);
+ }
+
+ private native String GetNotifyId(long specCallbackSDKptr);
+
+ /**
+ * @description: 对返回包计算签名&加密
+ * @param response: 待加密的回包明文.如果IsOk()==false,传入空串即可
+ * @note 本接口的执行问题可查看日志
+ * */
+ public void BuildResponseHeaderBody(String response) {
+ try {
+ responseHeaders = new HashMap();
+ responseBody = "";
+ BuildResponseHeaderBody(specCallbackSDKptr, response);
+ } catch (Exception e) {
+ SpecUtil.WWSpecLogError("SpecCallbackSDK exception caught", e.getMessage());
+ }
+ }
+
+ private native void BuildResponseHeaderBody(long specCallbackSDKptr, String response);
+
+ // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询
+ static {
+ try {
+ Class.forName("com.tencent.wework.SpecUtil");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
diff --git a/winboll/src/main/java/com/tencent/wework/SpecSDK.java b/winboll/src/main/java/com/tencent/wework/SpecSDK.java
new file mode 100644
index 0000000..8cf555a
--- /dev/null
+++ b/winboll/src/main/java/com/tencent/wework/SpecSDK.java
@@ -0,0 +1,163 @@
+package com.tencent.wework;
+
+/**
+ * @warning: 1. 不要修改成员变量名,native方法内有反射调用
+ * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内
+ * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件
+ */
+public final class SpecSDK {
+
+ /**
+ * @description 调用本地方法后实例化的对象指针
+ */
+ private long specSDKptr = 0;
+
+ /**
+ * @usage invoke的请求
+ * @example "{\"limit\":1}
+ */
+ private String request;
+
+ public void SetRequest(String request) {
+ this.request = request;
+ }
+
+ /**
+ * @usage 访问上一次invoke的结果
+ */
+ private String response;
+
+ public String GetResponse() {
+ return response;
+ }
+
+ /**
+ * @param corpid: 企业corpid,必选参数
+ * @param agentid: 应用id,必选参数
+ * @param ability_id: 能力ID,可选参数
+ * @param job_info: job_info,可选参数
+ * */
+ public SpecSDK(String corpId, long agentId) {
+ specSDKptr = NewSDK1(corpId, agentId);
+ }
+
+ private native long NewSDK1(String corpId, long agentId);
+
+ public SpecSDK(String corpId, long agentId, String abilityId) {
+ specSDKptr = NewSDK2(corpId, agentId, abilityId);
+ }
+
+ private native long NewSDK2(String corpId, long agentId, String abilityId);
+
+ public SpecSDK(String corpId, long agentId, String abilityId, String jobInfo) {
+ specSDKptr = NewSDK3(corpId, agentId, abilityId, jobInfo);
+ }
+
+ private native long NewSDK3(String corpId, long agentId, String abilityId, String jobInfo);
+
+ /**
+ * @description 使用callback的请求来初始化
+ * @param callback_sdk: 要求IsOk()==true
+ * @return C++内部指针,创建失败时指针仍为0,并输出错误日志
+ * */
+ public SpecSDK(SpecCallbackSDK callbackSDK) {
+ specSDKptr = NewSDK4(callbackSDK.GetPtr());
+ }
+
+ private native long NewSDK4(long callbackSDK);
+
+ /**
+ * @usage 在Java对象的内存回收前析构C++对象
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ DeleteCPPInstance(specSDKptr);
+ super.finalize();
+ }
+
+ private native void DeleteCPPInstance(long specSDKptr);
+
+ /**
+ * @description 用于在专区内调用企业微信接口
+ * @param api_name 接口名
+ * @param request json格式的请求数据
+ * @param response json格式的返回数据
+ * @return errorcode 参考如下:
+ * 0: 成功
+ * -910001: SDK没有初始化
+ * -910002: 没有设置请求体
+ * -910003: 没有设置请求的API
+ * -910004: 在SDK成员内找不到成员"response",注意lib内有反射机制,不要修改成员变量名
+ * -910005: 使用未初始化的callback初始化SDK
+ * -910006: invoke调用失败,应检查日志查看具体原因
+ * -910007: 响应体为空
+ * @note 当返回0时,表示没有网络或请求协议层面或调用方法的失败,
+ * 调用方需继续检查response中的errcode字段确保业务层面的成功
+ *
+ * @usage 当前版本sdk支持的接口列表,每个接口的具体协议请查看企业微信文档:
+ * https://developer.work.weixin.qq.com/document/path/91201
+ *
+ * +--------------------------------+--------------------------------+
+ * |接口名 |描述 |
+ * |--------------------------------|--------------------------------|
+ * |program_async_job_call_back |上报异步任务结果 |
+ * |sync_msg |获取会话记录 |
+ * |get_group_chat |获取内部群信息 |
+ * |get_agree_status_single |获取单聊会话同意情况 |
+ * |get_agree_status_room |获取群聊会话同意情况 |
+ * |set_hide_sensitiveinfo_config |设置成员会话组件敏感信息隐藏配置|
+ * |get_hide_sensitiveinfo_config |获取成员会话组件敏感信息隐藏配置|
+ * |search_chat |会话名称搜索 |
+ * |search_msg |会话消息搜索 |
+ * |create_rule |新增关键词规则 |
+ * |get_rule_list |获取关键词列表 |
+ * |get_rule_detail |获取关键词规则详情 |
+ * |update_rule |修改关键词规则 |
+ * |delete_rule |删除关键词规则 |
+ * |get_hit_msg_list |获取命中关键词规则的会话记录 |
+ * |create_sentiment_task |创建情感分析任务 |
+ * |get_sentiment_result |获取情感分析结果 |
+ * |create_summary_task |创建摘要提取任务 |
+ * |get_summary_result |获取摘要提取结果 |
+ * |create_customer_tag_task |创建标签匹配任务 |
+ * |get_customer_tag_result |获取标签任务结果 |
+ * |create_recommend_dialog_task |创建话术推荐任务 |
+ * |get_recommend_dialog_result |获取话术推荐结果 |
+ * |create_private_task |创建自定义模型任务 |
+ * |get_private_task_result |获取自定义模型结果 |
+ * |(废弃)document_list |获取知识集列表 |
+ * |create_spam_task |会话反垃圾创建分析任务 |
+ * |get_spam_result |会话反垃圾获取任务结果 |
+ * |create_chatdata_export_job |创建会话内容导出任务 |
+ * |get_chatdata_export_job_status |获取会话内容导出任务结果 |
+ * |spec_notify_app |专区通知应用 |
+ * |create_program_task |创建自定义程序任务 |
+ * |get_program_task_result |获取自定义程序结果 |
+ * |knowledge_base_list |获取企业授权给应用的知识集列表 |
+ * |knowledge_base_create |创建知识集 |
+ * |knowledge_base_detail |获取知识集详情 |
+ * |knowledge_base_add_doc |添加知识集內容 |
+ * |knowledge_base_remove_doc |删除知识集內容 |
+ * |knowledge_base_modify_name |修改知识集名称 |
+ * |knowledge_base_delete |删除知识集 |
+ * |search_contact_or_customer |员工或者客户名称搜索 |
+ * |create_ww_model_task |创建企微通用模型任务 |
+ * |get_ww_model_result |获取企微通用模型结果 |
+ * |get_msg_list_by_page_id |page_id获取消息列表 |
+ * +-----------------------------------------------------------------+
+ * */
+ public int Invoke(String apiName) {
+ return Invoke(specSDKptr, apiName, request);
+ }
+
+ private native int Invoke(long sdk, String apiName, String request);
+
+ // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询
+ static {
+ try {
+ Class.forName("com.tencent.wework.SpecUtil");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/winboll/src/main/java/com/tencent/wework/SpecUtil.java b/winboll/src/main/java/com/tencent/wework/SpecUtil.java
new file mode 100644
index 0000000..85ea23b
--- /dev/null
+++ b/winboll/src/main/java/com/tencent/wework/SpecUtil.java
@@ -0,0 +1,171 @@
+package com.tencent.wework;
+
+//import java.lang.management.ManagementFactory;
+//import java.lang.management.RuntimeMXBean;
+
+/**
+ * @warning: 1. 不要修改成员变量名,native方法内有反射调用
+ * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内
+ * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件
+ * 4. 使用其他工具打印的日志将无法被查询,如需使用SLF4j风格的日志或性能更好的日志框架,
+ * 请自行封装SpecUtil.SpecLog或SpecUtil.SpecLogNative方法
+ *
+ * @usage: 1. 获取SDK的版本号
+ * 2. 打印三个级别的日志
+ * 3. 开启调试模式
+ */
+public final class SpecUtil {
+
+ /**
+ * @description SDK版本号
+ * @usage 可用于校对不同SDK版本,或后续针对不同的SDK版本添加业务逻辑
+ */
+ private static final String SDK_VERSION = "1.4.0";
+
+ public static String GetSDKVersion() {
+ return SDK_VERSION;
+ }
+
+ /**
+ * @description 正确的包名,SDK必须存放在"com.tencent.wework"下,否则会影响本地方法的调用
+ */
+ private static final String EXPECTED_PACKAGE_NAME = "com.tencent.wework";
+
+ public static String GetExpectedPackageName() {
+ return EXPECTED_PACKAGE_NAME;
+ }
+
+ private static final String LINE_SEPERATOR = System.getProperty("line.separator");
+
+ public static void WWSpecLogInfo(String... args) {
+ SpecLog('I', args);
+ }
+
+ public static void WWSpecLogError(String... args) {
+ SpecLog('E', args);
+ }
+
+ public static void WWSpecLogDebug(String... args) {
+ SpecLog('D', args);
+ }
+
+ public static void WWSpecLogInfoWithReqId(String reqId, String... args) {
+ SpecLogWithReqId(reqId, 'I', args);
+ }
+
+ public static void WWSpecLogErrorWithReqId(String reqId, String... args) {
+ SpecLogWithReqId(reqId, 'E', args);
+ }
+
+ public static void WWSpecLogDebugWithReqId(String reqId, String... args) {
+ SpecLogWithReqId(reqId, 'D', args);
+ }
+
+ /**
+ * @usage 打印标准日志
+ * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看
+ * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG
+ * @param args 自定义参数
+ */
+ public static void SpecLog(char logLevel, String... args) {
+ StackTraceElement element = Thread.currentThread().getStackTrace()[3];
+ SpecLogNative(
+ logLevel,
+ element.getFileName(),
+ element.getLineNumber(),
+ String.join(",", args).replace(LINE_SEPERATOR, " ")
+ );
+ }
+
+ /**
+ * @usage 打印标准日志
+ * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看
+ * @param reqid 请求id
+ * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG
+ * @param args 自定义参数
+ */
+ public static void SpecLogWithReqId(String reqId, char logLevel, String... args) {
+ StackTraceElement element = Thread.currentThread().getStackTrace()[3];
+ SpecLogNativeWithReqId(
+ reqId,
+ logLevel,
+ element.getFileName(),
+ element.getLineNumber(),
+ String.join(",", args).replace(LINE_SEPERATOR, " ")
+ );
+ }
+
+ /**
+ * @usage 打印标准日志
+ * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看
+ * 如需SLF4J风格的接口或对日志性能有进一步需求,开发者可以自行封装该函数
+ * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG
+ * @param fileName 文件名(类名)
+ * @param lineNumber 行号
+ * @param argsString 自定义参数
+ */
+ public static native void SpecLogNative(char logLevel, String fileName, int lineNumber, String argsString);
+
+ /**
+ * @usage 打印标准日志
+ * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看
+ * 如需SLF4J风格的接口或对日志性能有进一步需求,开发者可以自行封装该函数
+ * @param reqid 请求id
+ * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG
+ * @param fileName 文件名(类名)
+ * @param lineNumber 行号
+ * @param argsString 自定义参数
+ */
+ public static native void SpecLogNativeWithReqId(String reqId, char logLevel, String fileName, int lineNumber, String argsString);
+
+
+
+ /**
+ * @usage 开启调试模式,进程级别开关
+ * @param debugToken 调试凭证,在管理端获取
+ * @param accessToken 应用access token
+ * @return 是否开启成功
+ */
+ public static boolean SpecOpenDebugMode(String debugToken, String accessToken) {
+ return SpecOpenDebugModeNative(debugToken, accessToken);
+ }
+
+ private static native boolean SpecOpenDebugModeNative(String debugToken, String accessToken);
+
+ /**
+ * @usage 生成notify id。用户可调用本接口生成notify id,也可完全自定义生成
+ * @return 新的notify id,支持纳秒级隔离,内部异常时会输出日志并返回空串
+ * @note 1. 用户可先生成notify id,将其与回调数据关联存储后,再使用该notify id通知应用,
+ * 从而保证回调数据被请求时已存储完毕
+ */
+ public static String GenerateNotifyId() {
+ return GenerateNotifyIdNative();
+ }
+
+ private static native String GenerateNotifyIdNative();
+
+ static {
+ // 检查包名
+ String packageName = SpecUtil.class.getPackage().getName();
+ if (!EXPECTED_PACKAGE_NAME.equals(packageName)) {
+ // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询
+ System.out.println("SpecUtil class must be in package com.tencent.wework");
+ System.exit(1);
+ }
+
+ // 加载so库
+ try {
+ System.loadLibrary("WeWorkSpecSDK");
+ } catch (UnsatisfiedLinkError e) {
+ System.out.println("libWeWorkSpecSDK.so not found in java.library.path");
+ e.printStackTrace();
+ System.exit(1);
+ } catch (Exception e) {
+ System.out.println("unexpected exception: " + e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ SpecUtil.WWSpecLogInfo("SDK init done", "packageName=" + packageName, "SDK_VERSION=" + SDK_VERSION);
+ }
+}
diff --git a/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml b/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml
new file mode 100644
index 0000000..b1448e8
--- /dev/null
+++ b/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/layout/activity_wxpay.xml b/winboll/src/main/res/layout/activity_wxpay.xml
new file mode 100644
index 0000000..bfd2596
--- /dev/null
+++ b/winboll/src/main/res/layout/activity_wxpay.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/menu/toolbar_main.xml b/winboll/src/main/res/menu/toolbar_main.xml
index 94e56bf..9054e7f 100644
--- a/winboll/src/main/res/menu/toolbar_main.xml
+++ b/winboll/src/main/res/menu/toolbar_main.xml
@@ -7,4 +7,7 @@
+