接口初步对接,正在调试数据处理方法...

This commit is contained in:
ZhanGSKen 2025-06-05 11:59:36 +08:00
parent a500decc7a
commit dee01f1179
10 changed files with 337 additions and 29 deletions

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jun 05 01:50:42 GMT 2025
#Thu Jun 05 03:57:29 GMT 2025
stageCount=7
libraryProject=libappbase
baseVersion=15.8
publishVersion=15.8.6
buildCount=38
buildCount=58
baseBetaVersion=15.8.7

View File

@ -24,5 +24,8 @@ dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
//
api 'com.squareup.okhttp3:okhttp:4.4.1'
api 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
// Gson依赖
api 'com.google.code.gson:gson:2.10.1'
}

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jun 05 01:50:42 GMT 2025
#Thu Jun 05 03:57:29 GMT 2025
stageCount=7
libraryProject=libappbase
baseVersion=15.8
publishVersion=15.8.6
buildCount=38
buildCount=58
baseBetaVersion=15.8.7

View File

@ -3,16 +3,18 @@ package cc.winboll.studio.libappbase.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.RadioButton;
import cc.winboll.studio.libappbase.BuildConfig;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.R;
import cc.winboll.studio.libappbase.models.UserInfoModel;
import cc.winboll.studio.libappbase.utils.RSAUtils;
import cc.winboll.studio.libappbase.utils.YunUtils;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import cc.winboll.studio.libappbase.utils.YunUtils;
import java.nio.charset.StandardCharsets;
/**
* @Author ZhanGSKen<zhangsken@188.com>
@ -23,6 +25,12 @@ public class LogonActivity extends Activity implements IWinBoLLActivity {
public static final String TAG = "LogonActivity";
public static final String DEBUG_HOST = "http://10.8.0.250:456";
public static final String YUN_HOST = "https://yun.winboll.cc";
String mHost = "";
RadioButton mrbYunHost;
RadioButton mrbDebugHost;
LogView mLogView;
@Override
@ -42,6 +50,25 @@ public class LogonActivity extends Activity implements IWinBoLLActivity {
mLogView = findViewById(R.id.logview);
mLogView.start();
mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST;
if (BuildConfig.DEBUG) {
mrbYunHost = findViewById(R.id.rb_yunhost);
mrbDebugHost = findViewById(R.id.rb_debughost);
mrbYunHost.setChecked(!BuildConfig.DEBUG);
mrbDebugHost.setChecked(BuildConfig.DEBUG);
} else {
findViewById(R.id.ll_hostbar).setVisibility(View.GONE);
}
}
public void onSwitchHost(View view) {
if (view.getId() == R.id.rb_yunhost) {
mrbDebugHost.setChecked(false);
mHost = YUN_HOST;
} else if (view.getId() == R.id.rb_debughost) {
mrbYunHost.setChecked(false);
mHost = DEBUG_HOST;
}
}
@Override
@ -52,9 +79,13 @@ public class LogonActivity extends Activity implements IWinBoLLActivity {
public void onTestLogin(View view) {
LogUtils.d(TAG, "onTestLogin");
YunUtils yunUtils = YunUtils.getInstance(this);
yunUtils.login();
yunUtils.checkLoginStatus();
final YunUtils yunUtils = YunUtils.getInstance(this);
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setUsername("jian");
userInfoModel.setPassword("kkiio");
userInfoModel.setToken("aaa111");
yunUtils.login(mHost, userInfoModel);
}
public void onTestRSA(View view) {
@ -113,4 +144,6 @@ public class LogonActivity extends Activity implements IWinBoLLActivity {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}

View File

@ -0,0 +1,38 @@
package cc.winboll.studio.libappbase.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/06/05 11:26
*/
import com.fasterxml.jackson.annotation.JsonProperty;
public class ResponseData {
@JsonProperty("status")
private String status;
@JsonProperty("message")
private String message;
// 原代码只有带参构造函数缺少无参构造函数
// public ResponseData(String status, String message) {
// this.status = status;
// this.message = message;
// }
// 添加无参构造函数Jackson 反序列化必需
public ResponseData() {
// 空实现
}
// 保留带参构造函数可选用于手动创建实例
public ResponseData(String status, String message) {
this.status = status;
this.message = message;
}
// Getter Setter
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

View File

@ -4,17 +4,21 @@ package cc.winboll.studio.libappbase.models;
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/06/04 19:14
*/
import cc.winboll.studio.libappbase.BaseBean;
import android.util.JsonWriter;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
public class UserInfoModel extends BaseBean {
public static final String TAG = "UserInfoModel";
@JsonProperty("username")
String username;
@JsonProperty("password")
String password;
@JsonProperty("token")
String token;
public UserInfoModel() {

View File

@ -0,0 +1,46 @@
package cc.winboll.studio.libappbase.utils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/06/05 11:09
*/
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.ResponseData;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URLDecoder;
public class JsonFormatter {
public static final String TAG = "JsonFormatter";
/**
* 将包含 Unicode 转义的 JSON 字符串转为可读中文
* @param jsonStr 原始 JSON 字符串{"status":"error","message":"\u672a\u63a5\u6536\u5230 JSON \u6570\u636e"}
* @return 格式化后的中文消息字符串
*/
public static String formatUnicodeToChinese(String jsonStr) {
try {
// 1. 使用 Jackson 解析 JSON 到对象
ObjectMapper mapper = new ObjectMapper();
ResponseData data = mapper.readValue(jsonStr, ResponseData.class);
// 2. 解码 Unicode 转义字符 \u672a 转为中文
String decodedMessage = URLDecoder.decode(data.getMessage(), "UTF-8");
// 3. 重新构建 JSON可选如需保留 JSON 格式
return "{\"status\":\"" + data.getStatus() + "\",\"message\":\"" + decodedMessage + "\"}";
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
return jsonStr; // 解析失败时返回原始字符串
}
}
// 示例用法
/*public static void main(String[] args) {
String rawJson = "{\"status\":\"error\",\"message\":\"\\u672a\\u63a5\\u6536\\u5230 JSON \\u6570\\u636e\"}";
String formattedJson = formatUnicodeToChinese(rawJson);
System.out.println("格式化前:" + rawJson);
System.out.println("格式化后:" + formattedJson);
}*/
}

View File

@ -6,6 +6,7 @@ package cc.winboll.studio.libappbase.utils;
* @Describe RSA加密工具
*/
import android.content.Context;
import android.util.Base64;
import cc.winboll.studio.libappbase.LogUtils;
import java.io.File;
import java.io.FileInputStream;
@ -196,5 +197,26 @@ public class RSAUtils {
byte[] decryptedBytes = cipher.doFinal(encryptedData);
return new String(decryptedBytes, "UTF-8");
}
/**
* HTTP 传输的 Base64 字符串还原为加密字节数组Java 7 兼容
* @param httpString Base64 字符串 null
* @return 加密字节数组
* @throws IllegalArgumentException 解码失败时抛出
*/
public byte[] httpStringToEncryptBytes(String httpString) {
Objects.requireNonNull(httpString, "HTTP 字符串不可为空");
// 计算缺失的填充符数量Java 7 不支持 repeat()手动拼接
int pad = httpString.length() % 4;
StringBuilder paddedString = new StringBuilder(httpString);
if (pad != 0) {
for (int i = 0; i < pad; i++) {
paddedString.append('='); // 补全 '='
}
}
// 使用 Base64 解码Android 原生 Base64 类兼容 Java 7
return Base64.decode(paddedString.toString(), Base64.URL_SAFE);
}
}

View File

@ -6,14 +6,25 @@ package cc.winboll.studio.libappbase.utils;
* @Describe 应用登录与接口工具
*/
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPModel;
import cc.winboll.studio.libappbase.models.UserInfoModel;
import com.google.gson.Gson;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class YunUtils {
public static final String TAG = "YunUtils";
@ -25,6 +36,12 @@ public class YunUtils {
String mDataFolderPath = "";
String mUserInfoModelPath = "";
private static final int CONNECT_TIMEOUT = 15; // 连接超时时间
private static final int READ_TIMEOUT = 20; // 读取超时时间
private static volatile YunUtils instance;
private OkHttpClient okHttpClient;
private Handler mainHandler; // 主线程 Handler
// 私有构造方法防止外部实例化
private YunUtils(Context context) {
LogUtils.d(TAG, "YunUtils");
@ -36,6 +53,11 @@ public class YunUtils {
}
mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson";
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();
mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper
}
// 公共静态方法返回唯一实例
@ -47,16 +69,6 @@ public class YunUtils {
return INSTANCE;
}
public void login() {
String token = getHostToken(null);
LogUtils.d(TAG, String.format("login token is %s", token));
saveLocalToken(token);
}
public void logout() {
}
public void checkLoginStatus() {
String token = getLocalToken();
LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token));
@ -67,12 +79,53 @@ public class YunUtils {
return (userInfoModel == null) ?"": userInfoModel.getToken();
}
String getHostToken(UserInfoModel userInfoModel) {
String token = "uuyyhh";
return token;
public void login(String host, UserInfoModel userInfoModel) {
LogUtils.d(TAG, "login");
// 发送 POST 请求
String apiUrl = host + "/login/index.php";
// 序列化对象为JSON
Gson gson = new Gson();
String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON
//String jsonData = userInfoModel.toString();
LogUtils.d(TAG, "要发送的数据 : " + jsonData);
sendPostRequest(apiUrl, jsonData, new OnResponseListener() {
// 成功回调主线程
@Override
public void onSuccess(String responseBody) {
LogUtils.d(TAG, "onSuccess");
try {
String formattedJson = JsonFormatter.formatUnicodeToChinese(responseBody);
LogUtils.d(TAG, formattedJson);
return;
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
try {
UserInfoModel userInfoModel = UserInfoModel.parseStringToBean(responseBody, UserInfoModel.class);
if (userInfoModel != null) {
LogUtils.d(TAG, "收到网站 UserInfoModel");
String token = userInfoModel.getToken();
saveLocalToken(token);
checkLoginStatus();
}
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
// 失败回调主线程
@Override
public void onFailure(String errorMsg) {
LogUtils.d(TAG, errorMsg);
// 处理错误
}
});
}
void saveLocalToken(String token) {
public void saveLocalToken(String token) {
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setToken(token);
saveUserInfoModel(userInfoModel);
@ -136,4 +189,88 @@ public class YunUtils {
LogUtils.d(TAG, "加密/解密失败: " + e.getMessage());
}
}
// 发送 POST 请求JSON 数据
public void sendPostRequest(String url, String data, OnResponseListener listener) {
RequestBody requestBody = RequestBody.create(
MediaType.parse("application/json; charset=utf-8"), // 关键头信息
data.getBytes(StandardCharsets.UTF_8)
);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("Content-Type", "application/json") // 显式添加头
.build();
executeRequest(request, listener);
}
// 发送 GET 请求
public void sendGetRequest(String url, OnResponseListener listener) {
Request request = new Request.Builder()
.url(url)
.get()
.build();
executeRequest(request, listener);
}
// 执行请求子线程处理
private void executeRequest(final Request request, final OnResponseListener listener) {
okHttpClient.newCall(request).enqueue(new Callback() {
// 响应成功子线程
@Override
public void onResponse(Call call, Response response) throws IOException {
try {
if (!response.isSuccessful()) {
postFailure(listener, "响应码错误:" + response.code());
return;
}
String responseBody = response.body().string();
postSuccess(listener, responseBody);
} catch (Exception e) {
postFailure(listener, "解析失败:" + e.getMessage());
}
}
// 响应失败子线程
@Override
public void onFailure(Call call, IOException e) {
postFailure(listener, "网络失败:" + e.getMessage());
}
// 主线程回调使用 Handler
private void postSuccess(final OnResponseListener listener, final String msg) {
mainHandler.post(new Runnable() {
@Override
public void run() {
listener.onSuccess(msg);
}
});
}
private void postFailure(final OnResponseListener listener, final String msg) {
mainHandler.post(new Runnable() {
@Override
public void run() {
listener.onFailure(msg);
}
});
}
});
}
public interface OnResponseListener {
/**
* 成功响应主线程回调
* @param responseBody 响应体字符串
*/
void onSuccess(String responseBody);
/**
* 失败回调包含错误信息
* @param errorMsg 错误描述
*/
void onFailure(String errorMsg);
}
}

View File

@ -6,6 +6,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:gravity="right"
android:padding="10dp"
android:id="@+id/ll_hostbar">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="10.8.0.250:456"
android:id="@+id/rb_debughost"
android:onClick="onSwitchHost"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="yun.winboll.cc"
android:id="@+id/rb_yunhost"
android:onClick="onSwitchHost"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"