上传文件至 /
This commit is contained in:
parent
4802e112fb
commit
ca07a146f3
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue