上传文件至 /
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