接口初步对接,正在调试数据处理方法...
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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' | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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,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()); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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; } | ||||
| } | ||||
|  | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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); | ||||
|     }*/ | ||||
| } | ||||
|  | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen