From dee01f1179ee501de1751cc3ead6d933589d0224 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Thu, 5 Jun 2025 11:59:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=88=9D=E6=AD=A5=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=EF=BC=8C=E6=AD=A3=E5=9C=A8=E8=B0=83=E8=AF=95=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=A4=84=E7=90=86=E6=96=B9=E6=B3=95...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appbase/build.properties | 4 +- libappbase/build.gradle | 5 +- libappbase/build.properties | 4 +- .../libappbase/activities/LogonActivity.java | 45 ++++- .../libappbase/models/ResponseData.java | 38 ++++ .../libappbase/models/UserInfoModel.java | 8 +- .../libappbase/utils/JsonFormatter.java | 46 +++++ .../studio/libappbase/utils/RSAUtils.java | 22 +++ .../studio/libappbase/utils/YunUtils.java | 169 ++++++++++++++++-- .../src/main/res/layout/activity_logon.xml | 25 +++ 10 files changed, 337 insertions(+), 29 deletions(-) create mode 100644 libappbase/src/main/java/cc/winboll/studio/libappbase/models/ResponseData.java create mode 100644 libappbase/src/main/java/cc/winboll/studio/libappbase/utils/JsonFormatter.java diff --git a/appbase/build.properties b/appbase/build.properties index 2302f48..c0a3b0c 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -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 diff --git a/libappbase/build.gradle b/libappbase/build.gradle index ddbcabc..4bf5023 100644 --- a/libappbase/build.gradle +++ b/libappbase/build.gradle @@ -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' + } diff --git a/libappbase/build.properties b/libappbase/build.properties index 2302f48..c0a3b0c 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -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 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/activities/LogonActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/activities/LogonActivity.java index 5833a6c..381fd88 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/activities/LogonActivity.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/activities/LogonActivity.java @@ -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 @@ -23,8 +25,14 @@ 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 public Activity getActivity() { return this; @@ -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()); } } + + } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/models/ResponseData.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/ResponseData.java new file mode 100644 index 0000000..8545706 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/ResponseData.java @@ -0,0 +1,38 @@ +package cc.winboll.studio.libappbase.models; + +/** + * @Author ZhanGSKen + * @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; } +} + diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/models/UserInfoModel.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/UserInfoModel.java index 03b4b4b..c3db193 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/models/UserInfoModel.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/UserInfoModel.java @@ -4,17 +4,21 @@ package cc.winboll.studio.libappbase.models; * @Author ZhanGSKen * @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() { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/JsonFormatter.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/JsonFormatter.java new file mode 100644 index 0000000..6505ba1 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/JsonFormatter.java @@ -0,0 +1,46 @@ +package cc.winboll.studio.libappbase.utils; + +/** + * @Author ZhanGSKen + * @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); + }*/ +} + diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/RSAUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/RSAUtils.java index 6bb73cc..e5c58c3 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/RSAUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/RSAUtils.java @@ -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); + } } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/YunUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/YunUtils.java index 21db4e4..585a837 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/YunUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/YunUtils.java @@ -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); + } } diff --git a/libappbase/src/main/res/layout/activity_logon.xml b/libappbase/src/main/res/layout/activity_logon.xml index f97b33b..2a2c2d1 100644 --- a/libappbase/src/main/res/layout/activity_logon.xml +++ b/libappbase/src/main/res/layout/activity_logon.xml @@ -6,6 +6,31 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + +