学习进度条

今日所花时间:一小时
今日代码量:100行
博客量:1篇
了解到的知识点: 手机端APP开发

对之前个人作业学习APP开发的代码优化
1.完成不同用户登录进入不同页面的功能
页面修改

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".LoginActivity">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="用户登录"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvStudentId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="学号:"
        android:layout_marginTop="24dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvTitle" />

    <EditText
        android:id="@+id/etStudentId"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:hint="输入账号"
        android:inputType="text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvStudentId" />

    <TextView
        android:id="@+id/tvPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="密码:"
        android:layout_marginTop="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/etStudentId" />

    <EditText
        android:id="@+id/etPassword"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:hint="输入密码"
        android:inputType="textPassword"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvPassword" />
    <!-- 在密码输入框和登录按钮之间添加角色选择 -->
    <RadioGroup
        android:id="@+id/rgRole"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/etPassword">

        <RadioButton
            android:id="@+id/rbStudent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="学生"
            android:checked="true"/>

        <RadioButton
            android:id="@+id/rbTeacher"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="教师"
            android:layout_marginLeft="16dp"/>
    </RadioGroup>

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_marginTop="100dp"
        android:text="登录"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/etPassword" />

    <Button
        android:id="@+id/btnRegister"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_marginTop="24dp"
        android:text="注册"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btnLogin" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_teacher_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".TeacherMainActivity">

    <!-- 标题 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="学生每日打卡记录"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="16dp"/>

    <!-- 表格标题行 -->
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:stretchColumns="*"
        android:background="#f0f0f0">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="学号"
                android:textStyle="bold"
                android:gravity="center"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="日期"
                android:textStyle="bold"
                android:gravity="center"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="PSP阶段"
                android:textStyle="bold"
                android:gravity="center"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="耗时(分钟)"
                android:textStyle="bold"
                android:gravity="center"/>
        </TableRow>
    </TableLayout>

    <!-- 记录列表 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:scrollbars="vertical"/>

    <!-- 按钮部分保持不变 -->
    <Button
        android:id="@+id/btnExport"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="导出数据"
        android:layout_marginTop="8dp"/>
    <Button
        android:id="@+id/btnSearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="每日总结查询"
        android:layout_marginTop="8dp"/>
    <Button
        android:id="@+id/btnStudentController"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="学生总结管理"
        android:layout_marginTop="8dp"/>
    <Button
        android:id="@+id/LoginOut"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="退出登录"
        android:layout_marginTop="8dp"/>
</LinearLayout>

LoginActivity,登录(根据不同用户)跳转

package com.example.study0327;

// LoginActivity.java

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.study0327.api.ApiService;
import com.example.study0327.api.RetrofitClient;
import com.example.study0327.pojo.ApiResponse;
import com.example.study0327.utils.JwtUtil;

import java.util.Map;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class LoginActivity extends AppCompatActivity {
    private EditText etStudentId, etPassword,etRole;
    private Button btnLogin, btnRegister;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);


        // 检查是否已登录
        if (isLoggedIn()) {
            // 获取存储的角色
            SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
            String role = prefs.getString("user_role", null);
            if (role != null) {
                navigateToMain(role);
            }
            return;
        }

        initViews();
        setupLoginButton();
        setupRegisterButton(); // 新增注册按钮事件绑定
    }

    private void initViews() {
        etStudentId = findViewById(R.id.etStudentId);
        etPassword = findViewById(R.id.etPassword);

        btnLogin = findViewById(R.id.btnLogin);
        btnRegister = findViewById(R.id.btnRegister); // 初始化注册按钮
    }

    private void setupLoginButton() {
        btnLogin.setOnClickListener(v -> attemptLogin());
    }

    private void setupRegisterButton() {
        btnRegister.setOnClickListener(v -> {
            // 跳转到注册页面
            Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
            startActivity(intent);
        });
    }
    // 获取选择的角色
    private String getSelectedRole() {
        RadioGroup rgRole = findViewById(R.id.rgRole);
        return rgRole.getCheckedRadioButtonId() == R.id.rbTeacher ? "TEACHER" : "STUDENT";
    }

    private void attemptLogin() {
        String studentId = etStudentId.getText().toString().trim();
        String password = etPassword.getText().toString().trim();
        String role = getSelectedRole(); // 获取用户选择的角色

        if (studentId.isEmpty() || password.isEmpty()) {
            Toast.makeText(this, "学号和密码不能为空", Toast.LENGTH_SHORT).show();
            return;
        }

        showLoading(true);
        ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
        Call<ApiResponse<String>> call = apiService.loginUser(studentId, password,role);

        call.enqueue(new Callback<ApiResponse<String>>() {
            @Override
            public void onResponse(Call<ApiResponse<String>> call, Response<ApiResponse<String>> response) {
                showLoading(false);

                if (response.isSuccessful() && response.body() != null) {
                    ApiResponse<String> apiResponse = response.body();
                    if (apiResponse.getCode() == 1) { // 登录成功
                        String token = apiResponse.getData();
                        // 解析Token获取角色(需确保后端JWT包含role字段)
                        Map<String, Object> claims = JwtUtil.parseToken(token); // 需实现JwtUtil解析
                        String role = (String) claims.get("role");
                        // 调试输出:确认角色值
                        Log.d("LoginDebug", "Role from token: " + role);
                        saveLoginState(token, studentId, role); // 传递studentId
                        navigateToMain(role);
                        Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(LoginActivity.this, apiResponse.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(LoginActivity.this, "登录失败: " + response.message(), Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<ApiResponse<String>> call, Throwable t) {
                showLoading(false);
                Toast.makeText(LoginActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private boolean isLoggedIn() {
        SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
        return prefs.contains("auth_token");
    }

    private void saveLoginState(String token, String studentId, String role) {
        SharedPreferences.Editor editor = getSharedPreferences("user_prefs", MODE_PRIVATE).edit();
        editor.putString("auth_token", token);
        editor.putString("student_id", studentId); // 保存student_id
        editor.putString("user_role", role); // 保存角色
        editor.putBoolean("is_logged_in", true);
        editor.apply();
    }

    private void navigateToMain(String role) {
        Intent intent;
        if ("TEACHER".equals(role)) {
            intent = new Intent(LoginActivity.this, TeacherMainActivity.class); // 教师专属主页
        } else {
            intent = new Intent(LoginActivity.this, MainActivity.class); // 学生主页
        }
        startActivity(intent);
        finish();
    }

    private void showLoading(boolean show) {
        // 这里可以添加加载动画的显示/隐藏逻辑
        btnLogin.setEnabled(!show);
    }


}

MainActivity

package com.example.study0327;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_CODE_SET_GOAL = 1001; // 添加这行定义请求码

    private Button btnLogin;
    private Button btnRegister;
    private LinearLayout layoutFunctions;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnLogin = findViewById(R.id.btnLogin);
        btnRegister = findViewById(R.id.btnRegister);
        layoutFunctions = findViewById(R.id.layoutFunctions);

        checkLoginStatus();

        btnLogin.setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, LoginActivity.class);
            startActivity(intent);
        });

        btnRegister.setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
            startActivity(intent);
        });
        // 在onCreate方法中添加
//        Button btnTeacherRegister = findViewById(R.id.btnTeacherRegister);
//        btnTeacherRegister.setOnClickListener(v -> {
//            Intent intent = new Intent(MainActivity.this, TeacherRegisterActivity.class);
//            startActivity(intent);
//        });
    }

    private void checkLoginStatus() {
        SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
        boolean isLoggedIn = prefs.getBoolean("is_logged_in", false);
        String role = prefs.getString("user_role", "STUDENT"); // 默认学生

        if (isLoggedIn) {
            if ("TEACHER".equals(role)) {
                startActivity(new Intent(this, TeacherMainActivity.class));
                finish();
            } else {
            btnLogin.setVisibility(View.GONE);
            btnRegister.setVisibility(View.GONE);
            layoutFunctions.setVisibility(View.VISIBLE);
            setupFunctionButtons();
            }
        } else {
            btnLogin.setVisibility(View.VISIBLE);
            btnRegister.setVisibility(View.VISIBLE);
            layoutFunctions.setVisibility(View.GONE);
        }
    }

    private void setupFunctionButtons() {
        findViewById(R.id.btnAddRecord).setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, AddRecordActivity.class);
            startActivity(intent);
        });

        findViewById(R.id.btnAddSummary).setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, AddSummaryActivity.class);
            startActivity(intent);
        });

        findViewById(R.id.btnSetGoal).setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, SetGoalActivity.class);
            startActivityForResult(intent, REQUEST_CODE_SET_GOAL); // 使用定义好的请求码
        });

        findViewById(R.id.btnLogout).setOnClickListener(v -> {
            logout();
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_SET_GOAL && resultCode == RESULT_OK) {
            Toast.makeText(this, "目标设置成功", Toast.LENGTH_SHORT).show();
        }
    }

    private void logout() {
        SharedPreferences.Editor editor = getSharedPreferences("user_prefs", MODE_PRIVATE).edit();
        editor.remove("auth_token");
        editor.putBoolean("is_logged_in", false);
        editor.apply();
        checkLoginStatus();
    }
    private void navigateBasedOnRole(String role) {
        Intent intent;
        if ("TEACHER".equals(role)) {
            intent = new Intent(this, TeacherMainActivity.class);
        } else {
            intent = new Intent(this, MainActivity.class);
        }
        startActivity(intent);
    }

}

TeacherMainActivity

package com.example.study0327;

import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Button;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.study0327.api.ApiService;
import com.example.study0327.api.RetrofitClient;
import com.example.study0327.modules.ProgrammingRecord;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class TeacherMainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private ProgrammingRecordAdapter adapter;
    private Button btnExport, btnSearch, btnStudentController, btnLogout;
    private static final int REQUEST_WRITE_EXTERNAL_STORAGE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_teacher_main);

        // 初始化视图
        recyclerView = findViewById(R.id.recyclerView);
        btnExport = findViewById(R.id.btnExport);
        btnSearch = findViewById(R.id.btnSearch);
        btnStudentController = findViewById(R.id.btnStudentController);
        btnLogout = findViewById(R.id.LoginOut);

        adapter = new ProgrammingRecordAdapter();
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        // 加载数据
        loadAllRecords();

        // 导出按钮点击事件
        btnExport.setOnClickListener(v -> exportData());

        // 查询按钮点击事件
        btnSearch.setOnClickListener(v -> {
            Intent intent = new Intent(TeacherMainActivity.this, DailySummarySearchActivity.class);
            startActivity(intent);
        });

        // 学生管理按钮点击事件
        btnStudentController.setOnClickListener(v -> {
            Toast.makeText(this, "跳转到学生总结管理", Toast.LENGTH_SHORT).show();
        });

        // 退出登录按钮点击事件
        btnLogout.setOnClickListener(v -> logout());
    }

    private void loadAllRecords() {
        String token = "Bearer " + getSharedPreferences("user_prefs", MODE_PRIVATE)
                .getString("auth_token", "");

        ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
        Call<List<ProgrammingRecord>> call = apiService.getAllProgrammingRecords(token);

        call.enqueue(new Callback<List<ProgrammingRecord>>() {
            @Override
            public void onResponse(Call<List<ProgrammingRecord>> call, Response<List<ProgrammingRecord>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    List<ProgrammingRecord> sortedRecords = response.body().stream()
                            .sorted((r1, r2) -> r2.getRecordDate().compareTo(r1.getRecordDate()))
                            .collect(Collectors.toList());
                    adapter.setRecords(sortedRecords);
                } else {
                    Toast.makeText(TeacherMainActivity.this, "获取数据失败", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<List<ProgrammingRecord>> call, Throwable t) {
                Toast.makeText(TeacherMainActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void exportData() {
        // 对于Android 10及以上版本,使用应用专属存储不需要权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10+可以直接写入应用专属目录
            showExportOptions();
        } else {
            // Android 9及以下需要请求权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        REQUEST_WRITE_EXTERNAL_STORAGE);
            } else {
                showExportOptions();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                showExportOptions();
            } else {
                Toast.makeText(this, "需要存储权限才能导出数据", Toast.LENGTH_SHORT).show();
                // 可选:解释为什么需要这个权限
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    new AlertDialog.Builder(this)
                            .setTitle("权限说明")
                            .setMessage("需要存储权限将数据导出到下载文件夹")
                            .setPositiveButton("确定", (dialog, which) ->
                                    ActivityCompat.requestPermissions(this,
                                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                            REQUEST_WRITE_EXTERNAL_STORAGE))
                            .setNegativeButton("取消", null)
                            .show();
                }
            }
        }
    }

    private void showExportOptions() {
        new AlertDialog.Builder(this)
                .setTitle("选择导出格式")
                .setItems(new String[]{"CSV格式", "Excel格式"}, (dialog, which) -> {
                    if (which == 0) {
                        exportToCSV();
                    } else {
                        exportToExcel();
                    }
                })
                .show();
    }

    private void exportToCSV() {
        if (adapter.getItemCount() == 0) {
            Toast.makeText(this, "没有数据可导出", Toast.LENGTH_SHORT).show();
            return;
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
        String fileName = "学生打卡记录_" + sdf.format(new Date()) + ".csv";

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10+使用应用专属目录
            File dir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
            File file = new File(dir, fileName);
            writeFile(file);
        } else {
            // Android 9及以下使用传统方法
            File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            File file = new File(downloadsDir, fileName);
            writeFile(file);
        }
    }

    private void writeFile(File file) {
        try {
            FileWriter writer = new FileWriter(file);
            writer.append("学号,日期,PSP阶段,耗时(分钟),内容\n");

            for (ProgrammingRecord record : adapter.getRecords()) {
                writer.append(String.format("%s,%s,%s,%d,%s\n",
                        record.getUserId(),
                        record.getRecordDate(),
                        record.getPspStage(),
                        record.getTimeSpent(),
                        record.getContent() != null ? record.getContent().replace(",", ",") : ""));
            }

            writer.flush();
            writer.close();

            Toast.makeText(this, "数据已导出到: " + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "导出失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void exportToExcel() {
        if (adapter.getItemCount() == 0) {
            Toast.makeText(this, "没有数据可导出", Toast.LENGTH_SHORT).show();
            return;
        }

        try {
            // 创建工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet sheet = workbook.createSheet("学生打卡记录");

            // 创建表头
            HSSFRow headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("学号");
            headerRow.createCell(1).setCellValue("日期");
            headerRow.createCell(2).setCellValue("PSP阶段");
            headerRow.createCell(3).setCellValue("耗时(分钟)");
            headerRow.createCell(4).setCellValue("内容");

            // 填充数据
            int rowNum = 1;
            for (ProgrammingRecord record : adapter.getRecords()) {
                HSSFRow row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(record.getUserId());
                row.createCell(1).setCellValue(record.getRecordDate());
                row.createCell(2).setCellValue(record.getPspStage());
                row.createCell(3).setCellValue(record.getTimeSpent());
                row.createCell(4).setCellValue(record.getContent() != null ? record.getContent() : "");
            }

            // 自动调整列宽
            for (int i = 0; i < 5; i++) {
                sheet.autoSizeColumn(i);
            }

            // 保存文件
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
            String fileName = "学生打卡记录_" + sdf.format(new Date()) + ".xls";
            File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            File file = new File(downloadsDir, fileName);

            FileOutputStream outputStream = new FileOutputStream(file);
            workbook.write(outputStream);
            workbook.close();
            outputStream.close();

            Toast.makeText(this, "数据已导出到: " + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "导出失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void logout() {
        SharedPreferences.Editor editor = getSharedPreferences("user_prefs", MODE_PRIVATE).edit();
        editor.remove("auth_token");
        editor.remove("student_id");
        editor.remove("user_role");
        editor.putBoolean("is_logged_in", false);
        editor.apply();

        Intent intent = new Intent(this, LoginActivity.class);
        startActivity(intent);
        finish();
    }
}

2.教师能够看见所有学生用户的每日打卡记录
ProgrammingRecordController

package com.example.studydemo.controller;

import com.example.studydemo.mapper.ProgrammingRecordMapper;
import com.example.studydemo.pojo.DailySummary;
import com.example.studydemo.pojo.ProgrammingRecord;
import com.example.studydemo.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/programmingRecord")
public class ProgrammingRecordController {

    @Autowired
    private ProgrammingRecordMapper programmingRecordMapper;

    @PostMapping("/addRecord")
    public ResponseEntity<String> addProgrammingRecord(@RequestBody ProgrammingRecord programmingRecord,
                                                       @RequestHeader("Authorization") String token) {
        try {
            // 从token中解析用户信息
            String studentId = JwtUtil.parseToken(token).get("student_id").toString();

            if (studentId == null) {
                return ResponseEntity.badRequest().body("未获取到用户学号");
            }

            // 设置用户ID为学号
            programmingRecord.setUserId(Long.parseLong(studentId));

            // 验证其他字段
            if (programmingRecord.getRecordDate() == null) {
                return ResponseEntity.badRequest().body("记录日期不能为空");
            }
            if (programmingRecord.getPspStage() == null || programmingRecord.getPspStage().trim().isEmpty()) {
                return ResponseEntity.badRequest().body("PSP阶段不能为空");
            }

            int insertedRows = programmingRecordMapper.insertProgrammingRecord(programmingRecord);
            if (insertedRows > 0) {
                return ResponseEntity.ok("编程记录添加成功");
            } else {
                return ResponseEntity.badRequest().body("编程记录添加失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.badRequest().body("添加编程记录出错: " + e.getMessage());
        }
    }

    @GetMapping("/list")
    public ResponseEntity<List<ProgrammingRecord>> listProgrammingRecord(@RequestHeader("Authorization") String token) {
        try {
            Map<String,Object> claims = JwtUtil.parseToken(token);
            String studentIdStr = (String) claims.get("student_id");
            if (studentIdStr == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
            }

            String studentId = String.valueOf(Long.parseLong(studentIdStr));
            List<ProgrammingRecord> record = programmingRecordMapper.selectProgrammingRecordByUserId(studentId);
            return ResponseEntity.ok(record);
        }catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    @GetMapping("/listAll")
    public ResponseEntity<List<ProgrammingRecord>> listAllRecords(
            @RequestHeader("Authorization") String token
    ) {
        try {
            // 验证Token并检查角色
            Map<String, Object> claims = JwtUtil.parseToken(token);
            String role = (String) claims.get("role");
            if (!"TEACHER".equals(role)) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }

            // 教师可查询所有记录
            List<ProgrammingRecord> records = programmingRecordMapper.selectAllRecords();
            return ResponseEntity.ok(records);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

ViewRecordsActivity

package com.example.study0327;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.study0327.api.ApiService;
import com.example.study0327.api.RetrofitClient;
import com.example.study0327.modules.ProgrammingRecord;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class ViewRecordsActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private ProgrammingRecordAdapter adapter;
    private String token;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_records);

        // 获取token
        SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
        token = "Bearer " + prefs.getString("auth_token", "").trim();

        // 初始化RecyclerView
        recyclerView = findViewById(R.id.recyclerViewRecords);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ProgrammingRecordAdapter();
        recyclerView.setAdapter(adapter);

        // 加载数据
        loadProgrammingRecords();
    }

    private void loadProgrammingRecords() {
        ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
        Call<List<ProgrammingRecord>> call = apiService.getProgrammingRecords(token);

        call.enqueue(new Callback<List<ProgrammingRecord>>() {
            @Override
            public void onResponse(Call<List<ProgrammingRecord>> call, Response<List<ProgrammingRecord>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    adapter.setRecords(response.body());
                } else {
                    Toast.makeText(ViewRecordsActivity.this, "获取记录失败: " + response.message(), Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<List<ProgrammingRecord>> call, Throwable t) {
                Toast.makeText(ViewRecordsActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}
posted @ 2025-04-07 20:42  haoyinuo  阅读(39)  评论(0)    收藏  举报