上传文件至 /

This commit is contained in:
XuYuqi 2025-07-11 18:34:06 +08:00
parent 4802e112fb
commit ca07a146f3
3 changed files with 4969 additions and 0 deletions

96
AdminActivity.java Normal file
View File

@ -0,0 +1,96 @@
package com.example.myapplication;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.DataBase.DatabaseHelper;
public class AdminActivity extends AppCompatActivity {
private ListView lvUsers;
private DatabaseHelper dbHelper;
private SimpleCursorAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_admin);
dbHelper = new DatabaseHelper(this);
lvUsers = findViewById(R.id.lvUsers);
Button btnBack = findViewById(R.id.btnBack);
loadUsers();
btnBack.setOnClickListener(v -> finish());
}
private void loadUsers() {
Cursor cursor = dbHelper.getAllUsers();
String[] from = new String[]{
DatabaseHelper.COLUMN_USERNAME,
DatabaseHelper.COLUMN_PASSWORD,
DatabaseHelper.COLUMN_USER_PROJECT_NAME,
DatabaseHelper.COLUMN_USER_PROJECT_ID
};
int[] to = new int[]{
R.id.tvUsername,
R.id.tvPassword,
R.id.tvProjectName,
R.id.tvProjectId
};
adapter = new SimpleCursorAdapter(this, R.layout.user_item,
cursor, from, to, 0) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
// 获取当前行的数据
Cursor cursor = (Cursor) getItem(position);
@SuppressLint("Range") String username = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_USERNAME));
// 设置删除按钮点击事件
Button btnDelete = view.findViewById(R.id.btnDelete);
btnDelete.setOnClickListener(v -> {
if (dbHelper.deleteUser(username)) {
Toast.makeText(AdminActivity.this, "用户已删除", Toast.LENGTH_SHORT).show();
// 重新加载数据
Cursor newCursor = dbHelper.getAllUsers();
changeCursor(newCursor);
} else {
Toast.makeText(AdminActivity.this, "删除失败", Toast.LENGTH_SHORT).show();
}
});
TextView tvProjectName = view.findViewById(R.id.tvProjectName);
tvProjectName.setOnClickListener(v -> {
if (tvProjectName.getMaxLines() == 1) {
tvProjectName.setMaxLines(Integer.MAX_VALUE); // 展开
} else {
tvProjectName.setMaxLines(1); // 收起
}
});
return view;
}
};
lvUsers.setAdapter(adapter);
}
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}
}

853
LoginActivity.java Normal file
View File

@ -0,0 +1,853 @@
package com.example.myapplication;
import static android.content.ContentValues.TAG;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.DataBase.DatabaseHelper;
import com.example.myapplication.Tool.AuthInterceptor;
import com.example.myapplication.Tool.RetrofitClient;
import com.example.myapplication.Tool.ShowError;
import com.example.myapplication.api.AuthApi;
import com.example.myapplication.api.ProjectApi;
import com.example.myapplication.api.UserApi;
import com.example.myapplication.model.ApiResponse;
import com.example.myapplication.model.LoginEntity;
import com.example.myapplication.model.LoginRequest;
import com.example.myapplication.model.PageResult;
import com.example.myapplication.model.Project;
import com.example.myapplication.model.UserInfo;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
public class LoginActivity extends AppCompatActivity {
private EditText etUsername, etPassword;
private CheckBox cbRemember;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final ExecutorService executor = Executors.newFixedThreadPool(7);
private Button btnClearProject;
private Button btnLogin;
private DatabaseHelper dbHelper;
private SharedPreferences sharedPreferences;
private final String AUTH_TOKEN_KEY = "auth_token";
private static final String Name = "name";
private static final String BASE_URL = "http://pms.dtyx.net:9158/";
private String authToken = "";
private static final String PREFS_NAME = "LoginPrefs";
private static final String PREF_USERNAME = "username";
private static final String PREF_PASSWORD = "password";
private static final String PREF_REMEMBER = "remember";
private static final String PREF_PROJECT = "project"; // 新增项目键名
private AutoCompleteTextView actvProject;
private ArrayAdapter<Project> projectAdapter;
private TextView tvSelectedProjectName;
private TextView tvSelectedProjectId;
private List<Project> projectList = new ArrayList<>();
ShowError errorShower = new ShowError(this);
@SuppressLint("SuspiciousIndentation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
dbHelper = new DatabaseHelper(this);
// 使用 Context.MODE_PRIVATE 替代 MODE_PRIVATE 常量
sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
initViews();
setupListeners();
loadSavedPreferences();
loadProjects();
}
private void initViews() {
etUsername = findViewById(R.id.etUsername);
etPassword = findViewById(R.id.etPassword);
actvProject = findViewById(R.id.etProject);
cbRemember = findViewById(R.id.cbRemember);
btnLogin = findViewById(R.id.btnLogin);
btnClearProject = findViewById(R.id.btnClearProject);
tvSelectedProjectName = findViewById(R.id.tvSelectedProjectName);
tvSelectedProjectId = findViewById(R.id.tvSelectedProjectId);
// 初始化项目下拉框
initProjectDropdown();
}
private void setupListeners() {
// 清空项目按钮点击事件
btnClearProject.setOnClickListener(v -> {
actvProject.setText("");
tvSelectedProjectName.setText("项目名称:未选择");
tvSelectedProjectId.setText("项目ID未选择");
loadProjects();
projectAdapter.getFilter().filter("");
actvProject.showDropDown();
});
btnLogin.setOnClickListener(v -> attemptLogin());
}
private boolean isNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) return false;
NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork());
return capabilities != null &&
(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));
}
private void loadSavedPreferences() {
boolean remember = sharedPreferences.getBoolean(PREF_REMEMBER, false);
if (remember) {
String savedUsername = sharedPreferences.getString(PREF_USERNAME, "");
String savedPassword = sharedPreferences.getString(PREF_PASSWORD, "");
String savedProject = sharedPreferences.getString(PREF_PROJECT, "");
etUsername.setText(savedUsername);
etPassword.setText(savedPassword);
actvProject.setText(savedProject);
if (!savedProject.isEmpty()) {
try {
int lastBracketIndex = savedProject.lastIndexOf("(");
int closingBracketIndex = savedProject.lastIndexOf(")");
if (lastBracketIndex != -1 && closingBracketIndex != -1 && closingBracketIndex > lastBracketIndex) {
String projectName = savedProject.substring(0, lastBracketIndex).trim();
String projectId = savedProject.substring(lastBracketIndex + 1, closingBracketIndex).trim();
tvSelectedProjectName.setText("项目名称:" + projectName);
tvSelectedProjectId.setText("项目ID:" + projectId);
}
} catch (Exception e) {
e.printStackTrace();
tvSelectedProjectName.setText("解析错误");
tvSelectedProjectId.setText("");
}
}
cbRemember.setChecked(true);
}
}
private void attemptLogin() {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
String projectInput = actvProject.getText().toString().trim();
if (username.isEmpty() || password.isEmpty() || projectInput.isEmpty()) {
Toast.makeText(LoginActivity.this, "请填写所有字段", Toast.LENGTH_SHORT).show();
return;
}
// 在后台线程执行数据库操作
// 先进行认证
if(isNetworkAvailable())
authenticateUser(username, password, projectInput);
else continueLoginProcess(username,password,projectInput);
}
private void authenticateUser(String username, String password, String projectInput) {
executor.execute(() -> {
try {
// 加密密码
String encryptedPassword2 = encryptPassword(username, password);
// 创建认证请求体
LoginRequest loginRequest = new LoginRequest(username, encryptedPassword2);
String token = sharedPreferences.getString(AUTH_TOKEN_KEY, "");
// 初始化Retrofit
Retrofit retrofit = RetrofitClient.getClient(token);
// 创建认证API服务
AuthApi authApi = retrofit.create(AuthApi.class);
// 执行认证请求
Call<ApiResponse<LoginEntity>> call = authApi.login(loginRequest);
Response<ApiResponse<LoginEntity>> response = call.execute();
if (response.isSuccessful() && response.body() != null) {
if (response.body().getCode() == 500200) {
//修改密码操作
runOnUiThread(() -> showChangePasswordDialog(username));
} else if (response.body().getCode() == 200) {
authToken = response.body().getData().getTokenValue();
Log.e("令牌:",authToken);
sharedPreferences.edit().putString(AUTH_TOKEN_KEY, authToken).apply();
Retrofit retrofit2 = RetrofitClient.getClient(authToken);
// 创建UserApi服务
UserApi userApi = retrofit2.create(UserApi.class);
// 执行查询请求只根据account查询
Call<PageResult<UserInfo>> userCall = userApi.getUserList(
authToken, // 注意token前缀根据你的API要求调整
username, // account参数
null, // deptId
null, // mobile
null, // name
null, // userCode
null, // userStatus
null // userType
);
try {
Response<PageResult<UserInfo>> userResponse = userCall.execute();
if (userResponse.isSuccessful() && userResponse.body() != null
&& userResponse.body().getCode() == 200
&& !userResponse.body().getRows().isEmpty()) {
// 获取第一个匹配用户的name
String name = userResponse.body().getRows().get(0).getName();
sharedPreferences.edit().putString(Name, name).apply();
executor.execute(()-> {
continueLoginProcess(username, password, projectInput);
});
} else {
// 查询失败或没有结果使用username作为name
errorShower.showErrorDialog("查询姓名失败" , userResponse.body().getMsg());
sharedPreferences.edit().putString(Name, "").apply();
executor.execute(()-> {
continueLoginProcess(username, password, projectInput);
});
}
} catch (IOException e) {
e.printStackTrace();
// 发生异常使用username作为name
errorShower.showErrorDialog("出现异常:", e.getMessage());
executor.execute(()-> {
continueLoginProcess(username, password, projectInput);
});
}
}
else {
showToast(password+"密码出错"+response.body().toString());
}
} else {
runOnUiThread(() -> {
String errorMsg = "认证失败: ";
if (response.errorBody() != null) {
try {
errorMsg += response.errorBody().string();
} catch (IOException e) {
errorMsg += "无法读取错误详情";
}
}
showToast(errorMsg);
});
}
} catch (Exception e) {
showToast("认证出错:"+e.getMessage());
}
});
}
// 封装 Toast 显示
private void showToast(String message) {
mainHandler.post(() -> Toast.makeText(LoginActivity.this, message, Toast.LENGTH_SHORT).show());
}
private void showChangePasswordDialog(String username) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("请修改密码");
View view = getLayoutInflater().inflate(R.layout.dialog_change_password, null);
EditText etOldPassword = view.findViewById(R.id.etOldPassword);
EditText etNewPassword = view.findViewById(R.id.etNewPassword);
EditText etConfirmPassword = view.findViewById(R.id.etConfirmPassword);
builder.setView(view);
builder.setPositiveButton("确认", (dialog, which) -> {
String oldPassword = etOldPassword.getText().toString().trim();
String newPassword = etNewPassword.getText().toString().trim();
String confirmPassword = etConfirmPassword.getText().toString().trim();
if (newPassword.equals(confirmPassword)) {
changePassword(username, oldPassword, newPassword);
} else {
Toast.makeText(this, "新密码与确认密码不匹配", Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton("取消", null);
builder.setCancelable(false);
builder.show();
}
private void changePassword(String username, String oldPassword, String newPassword) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
// 加密旧密码和新密码
String encryptedOldPassword = encryptPassword(username, oldPassword);
String encryptedNewPassword = encryptPassword(username, newPassword);
// 创建修改密码请求体
ChangePasswordRequest request = new ChangePasswordRequest(
username,
encryptedNewPassword,
encryptedOldPassword
);
// 获取token
String token = sharedPreferences.getString(AUTH_TOKEN_KEY, "");
// 初始化Retrofit
Retrofit retrofit = RetrofitClient.getClient(token);
// 创建API服务
AuthApi authApi = retrofit.create(AuthApi.class);
// 执行修改密码请求
Call<ApiResponse<Void>> call = authApi.modifyPassword(request);
Response<ApiResponse<Void>> response = call.execute();
runOnUiThread(() -> {
if (response.isSuccessful() && response.body() != null) {
if (response.body().isSuccess()) {
Toast.makeText(LoginActivity.this, "密码修改成功,请重新登录", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(LoginActivity.this, "密码修改失败: " + response.body().toString(), Toast.LENGTH_SHORT).show();
}
} else {
String errorMsg = "修改密码失败: ";
if (response.errorBody() != null) {
try {
errorMsg += response.errorBody().string();
} catch (IOException e) {
errorMsg += "无法读取错误详情";
}
}
Toast.makeText(LoginActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
runOnUiThread(() ->
Toast.makeText(LoginActivity.this, "修改密码出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
});
}
// 新增修改密码请求体
public static class ChangePasswordRequest {
private String account;
private String newPassword;
private String oldPassword;
public ChangePasswordRequest(String account, String newPassword, String oldPassword) {
this.account = account;
this.newPassword = newPassword;
this.oldPassword = oldPassword;
}
// getters and setters
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
public String getOldPassword() {
return oldPassword;
}
public void setOldPassword(String oldPassword) {
this.oldPassword = oldPassword;
}
}
private void continueLoginProcess(String username, String password, String projectInput) {
// 在后台线程执行数据库操作
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
// 解析项目ID和名称
Pair<String, String> projectInfo = parseProjectInfo(projectInput);
String projectId = projectInfo.first;
String projectName = projectInfo.second;
// 保存登录信息
saveLoginPreferences(username, password, projectName, projectId);
// 检查用户是否存在
boolean userExists = dbHelper.userExists(username);
runOnUiThread(() -> {
if (userExists) {
// 用户存在验证密码
if (dbHelper.checkUser(username, password)) {
handleLoginSuccess(username, projectId, projectName,password);
} else {
Toast.makeText(LoginActivity.this, "密码错误", Toast.LENGTH_SHORT).show();
}
} else {
// 用户不存在自动注册
if (dbHelper.addUser(username, password, projectId, projectName)) {
handleLoginSuccess(username, projectId, projectName,password);
} else {
Toast.makeText(LoginActivity.this, "注册失败", Toast.LENGTH_SHORT).show();
}
}
});
} catch (Exception e) {
runOnUiThread(() ->
Toast.makeText(LoginActivity.this, "登录出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
});
}
private String encryptPassword(String username, String password) {
try {
// 1. 对账号做MD5计算
String md5Hash = md5(username);
// 2. 取8-24位作为密钥
String key = md5Hash.substring(8, 24); // 8-24位索引从0开始
Log.e("key:",key);
// 3. AES加密
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // PKCS5Padding等同于PKCS7Padding在Java中
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(password.getBytes());
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT).trim();
} catch (Exception e) {
e.printStackTrace();
showToast("加密密码错误:"+e.getMessage());
return "";
}
}
private String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private Pair<String, String> parseProjectInfo(String projectInput) {
String projectId = "";
String projectName = "";
Matcher matcher = Pattern.compile(".*\\((.*)\\)").matcher(projectInput);
if (matcher.find()) {
projectId = matcher.group(1).trim();
projectName = projectInput.replace("(" + projectId + ")", "").trim();
} else {
for (Project project : projectList) {
if (project.getProjectName().equals(projectInput) ||
project.getProjectId().equals(projectInput)) {
projectId = project.getProjectId();
projectName = project.getProjectName();
break;
}
}
if (projectId.isEmpty()) {
projectName = projectInput;
}
}
return new Pair<>(projectId, projectName);
}
private void saveLoginPreferences(String username, String password, String projectName, String projectId) {
SharedPreferences.Editor editor = sharedPreferences.edit();
if (cbRemember.isChecked()) {
editor.putString(PREF_USERNAME, username);
editor.putString(PREF_PASSWORD, password);
String projectDisplayText = projectId.isEmpty() ? projectName :
(projectName + " (" + projectId + ")");
editor.putString(PREF_PROJECT, projectDisplayText);
editor.putBoolean(PREF_REMEMBER, true);
} else {
editor.clear();
}
editor.apply();
}
private void handleLoginSuccess(String username, String projectId, String projectName,String password) {
dbHelper.updateUserProject(username, projectId, projectName);
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra("username", username);
intent.putExtra("password",password);
intent.putExtra("projectId", projectId);
intent.putExtra("projectName", projectName);
startActivity(intent);
finish();
}
private void initProjectDropdown() {
projectAdapter = new ArrayAdapter<Project>(this,
R.layout.custom_dropdown_item,
projectList) {
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView textView = view.findViewById(android.R.id.text1);
Project project = getItem(position);
if (project != null) {
textView.setText(project.getProjectName() + " (" + project.getProjectId() + ")");
textView.setSingleLine(false);
textView.setMaxLines(3);
textView.setEllipsize(null);
}
return view;
}
@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
return getView(position, convertView, parent);
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Project> filteredList = new ArrayList<>();
if (constraint != null && constraint.length() > 0) {
String filterPattern = constraint.toString().toLowerCase().trim();
DatabaseHelper dbHelper = new DatabaseHelper(LoginActivity.this);
filteredList = dbHelper.searchProjects(filterPattern);
} else {
filteredList.addAll(projectList);
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
if (results.values != null) {
addAll((List<Project>) results.values);
}
notifyDataSetChanged();
}
};
}
};
actvProject.setAdapter(projectAdapter);
actvProject.setThreshold(0);
actvProject.setDropDownBackgroundResource(android.R.color.white);
actvProject.setOnItemClickListener((parent, view, position, id) -> {
Object item = parent.getItemAtPosition(position);
if (item instanceof Project) {
Project selectedProject = (Project) item;
tvSelectedProjectName.setText("项目名称:" + selectedProject.getProjectName());
tvSelectedProjectId.setText("项目ID" + selectedProject.getProjectId());
actvProject.setText(selectedProject.getProjectName() + " (" + selectedProject.getProjectId() + ")");
} else {
Log.e("TYPE_ERROR", "Expected Project but got: " + item.getClass());
Toast.makeText(this, "数据格式错误", Toast.LENGTH_SHORT).show();
}
});
}
private void loadProjects() {
try {
// 1. 验证接口定义
if (!validateApiDefinition()) {
return;
}
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS);
okHttpClientBuilder.addInterceptor(new AuthInterceptor(authToken));
// 2. 初始化Retrofit
Retrofit retrofit = RetrofitClient.getClient(authToken);
// 3. 创建API服务
ProjectApi projectApi = retrofit.create(ProjectApi.class);
// 4. 执行网络请求
executeProjectRequest(projectApi);
} catch (Exception e) {
showErrorOnUI("初始化失败", getErrorMessage(e));
logErrorToFile(e);
}
}
private boolean validateApiDefinition() {
try {
Method method = ProjectApi.class.getMethod("getProjectList");
Type returnType = method.getGenericReturnType();
// 预期的完整类型签名
String expectedType = "retrofit2.Call<com.example.myapplication.model.ApiResponse<java.util.List<com.example.myapplication.model.Project>>>";
if (!returnType.toString().equals(expectedType)) {
String errorMsg = "接口定义错误!\n\n当前定义:\n" + returnType +
"\n\n应修改为:\n@GET(\"project/list\")\nCall<ApiResponse<List<Project>>> getProjectList();";
new Handler(Looper.getMainLooper()).post(() -> {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("API接口定义不合法")
.setMessage(errorMsg)
.setPositiveButton("复制定义", (d, w) -> copyToClipboard(errorMsg))
.setNegativeButton("关闭", null)
.show();
});
return false;
}
return true;
} catch (Exception e) {
showErrorOnUI("接口验证失败", e.getMessage());
return false;
}
}
private void executeProjectRequest(ProjectApi projectApi) {
Call<ApiResponse<List<Project>>> call = projectApi.getProjectList();
call.enqueue(new Callback<ApiResponse<List<Project>>>() {
@Override
public void onResponse(Call<ApiResponse<List<Project>>> call, Response<ApiResponse<List<Project>>> response) {
if (response.isSuccessful() && response.body() != null) {
handleSuccessResponse(response.body().getData());
} else {
handleApiError(response);
}
}
@Override
public void onFailure(Call<ApiResponse<List<Project>>> call, Throwable t) {
handleNetworkError(t);
}
});
}
// === 错误处理方法 ===
private void handleSuccessResponse(List<Project> projects) {
runOnUiThread(() -> {
try {
dbHelper.saveProjects(projects);
projectList.clear();
projectList.addAll(projects);
projectAdapter.notifyDataSetChanged();
} catch (Exception e) {
showErrorOnUI("数据处理错误", e.getMessage());
}
});
}
private void handleApiError(Response<?> response) {
String errorMsg = "服务器响应错误: " + response.code();
try {
if (response.errorBody() != null) {
errorMsg += "\n" + response.errorBody().string();
}
} catch (IOException e) {
errorMsg += "\n无法读取错误详情";
}
showErrorOnUI("API请求失败", errorMsg);
loadCachedProjects();
}
private void handleNetworkError(Throwable t) {
String errorMsg = "网络错误: " + t.getMessage();
if (t instanceof SocketTimeoutException) {
errorMsg = "连接超时,请检查网络";
} else if (t instanceof UnknownHostException) {
errorMsg = "无法解析主机请检查URL";
}
showErrorOnUI("网络连接失败", errorMsg);
loadCachedProjects();
}
// === 工具方法 ===
private void showErrorOnUI(String title, String message) {
runOnUiThread(() -> {
// 在界面底部显示错误面板
ViewGroup rootView = findViewById(android.R.id.content);
LinearLayout errorPanel = new LinearLayout(this);
errorPanel.setOrientation(LinearLayout.VERTICAL);
errorPanel.setBackgroundColor(0x33FF0000);
errorPanel.setPadding(32, 16, 32, 16);
TextView tvError = new TextView(this);
tvError.setTextColor(Color.RED);
tvError.setText(title);
TextView tvMsg = new TextView(this);
tvMsg.setText(message);
errorPanel.addView(tvError);
errorPanel.addView(tvMsg);
rootView.addView(errorPanel);
// 5秒后自动消失
new Handler().postDelayed(() -> rootView.removeView(errorPanel), 5000);
});
}
private void copyToClipboard(String text) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(ClipData.newPlainText("错误定义", text));
Toast.makeText(this, "定义已复制", Toast.LENGTH_SHORT).show();
}
private String getErrorMessage(Exception e) {
if (e instanceof IllegalArgumentException) {
return "参数错误: " + e.getMessage();
} else if (e instanceof NullPointerException) {
return "空指针异常: " + e.getMessage();
} else {
return "未知错误: " + e.getClass().getSimpleName();
}
}
private void logErrorToFile(Exception e) {
String log = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +
"\nError: " + e.getClass().getName() +
"\nMessage: " + e.getMessage() +
"\nStack Trace:\n" + Log.getStackTraceString(e);
try {
File file = new File(getExternalFilesDir(null), "error_log.txt");
FileWriter writer = new FileWriter(file, true);
writer.append(log).append("\n\n");
writer.close();
} catch (IOException ioException) {
Log.e("FileLog", "无法写入错误日志", ioException);
}
}
private void loadCachedProjects() {
new Thread(() -> {
List<Project> cachedProjects = dbHelper.getAllProjects();
runOnUiThread(() -> {
projectList.clear();
projectList.addAll(cachedProjects);
projectAdapter.notifyDataSetChanged();
Toast.makeText(this, "已加载本地缓存数据", Toast.LENGTH_LONG).show();
});
}).start();
}
}

4020
MainActivity.java Normal file

File diff suppressed because it is too large Load Diff