立即注册

ekn

2019人工智能创新平台招新仅注册用户才可以浏览全站
查看: 246|回复: 6

基于springboot的学生成绩管理系统

[复制链接]

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
发表于 2024-10-20 20:42:43 | 显示全部楼层 |阅读模式
基于springboot的学生成绩管理系统
回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-10-25 22:59:15 | 显示全部楼层

因为帖子长度限制,所以以附件形式上传

本帖最后由 08060402宋子怡 于 2024-10-25 23:02 编辑

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-11-1 17:28:41 | 显示全部楼层
本周我做了学生端的功能设计和实现
1.学生模块需求分析:
学生可以查看和完善自己的个人信息,如年龄、性别、证件号、班级、入学日期、专业班级、用户头像等。学生应可以查询自己的成绩信息,包括课程名称、成绩分数、绩点等,支持按学期、课程类型等条件进行成绩查询。对于有异议的成绩应该提供成绩复核申请的功能。
2.学生模块功能设计
(1)查看个人信息: 学生可以在个人首页查看自己的基本信息,包括手机号、邮箱、姓名、学号、年龄、性别、证件号、专业、班级等。
(2)编辑个人信息: 学生可以在个人首页修改个人信息,可以修改的内容包括头像、年龄、性别、证件号、专业、班级等。
(3)查看个人学期成绩: 学生可以按照学期查询个人成绩,可以查看课程名、成绩、专业排名。
(4)查看绩点排名:查看所属专业总绩点排名前50名同学。
(5)查看成绩可视化报告:学生可以查看自己成绩的分析报告,包括学期绩点、绩点排名、在校成绩区间图、专业基础课程和专业核心课程成绩分析。
(6)申请成绩复核:学生可以对有疑问的成绩提出复核申请,并查看申请状态。
(7)选课:学生可以在选课通知发布后进行课程选择,系统会显示可选课程列表,学生根据个人计划进行选课。
3.学生模块功能实现
3.1 查看个人信息
当用户访问个人首页时,前端通Vuex状态管理库获取当前登录用户的信息this.$store.state.user.user,当前端get请求用户信息从后端接口/getInfo获得用户信息。后端SecurityUtils.getSubject().getPrincipal()来获取当前登录用户的详细信息,并返回给前端。同时前端使用Element UI的el-descriptions组件展示用户的详细信息,包括手机号、邮箱、年龄等。使用el-card组件展示用户的头像、姓名、学号、平均绩点和专业排名。

3.2 编辑个人信息
学生可以在个人首页点击“完善信息”,可以在el-dialog组件弹出编辑信息的对话框修改的内容包括年龄、性别、证件号、专业、班级等,用户点击“确定”按钮后,前端通过el-form组件收集表单数据,并使用Axios发送POST请求到后端的/stu/updateStu接口,后端使用BeanUtils.copyProperties方法将前端传来的VoUser对象的属性复制到一个新的StUser实体对象中,userService.saveOrUpdate方法将进过校验的StUser 对象保存到数据库中,并返回更新成功或失败的提示。

3.3 查看个人学期成绩
学生通过选择学年来查看个人的学期成绩,学年数据通过 menuInit 方法传递到后端接口 /stu/room/menuInit ,填充到 yearData 数组中,学生选择学年后点击查询按钮触发 getGrade 方法,后端接口 /grade/getGrade 并传递所选学年参数 stuYear 来获取用户的成绩数据。


3.4 查看绩点排名
前端使用使用el-table组件展示学生的成绩排名。el-table-column定义了表格的列,包括学号、姓名、专业、课程名、成绩和专业排名。每一列都绑定到rankList数组中对象的相应属性,后端通过指定major_name专业名称或者指定课程编号couId查询所属专业或者课程总绩点前50名同学排名。

3.5 查看成绩可视化报告
前端通过GET方法分别向后端接口/scoreAnalysis/getRankAndGpa、/scoreAnalysis/getCourseByType、/scoreAnalysis/getGradeInterval、/scoreAnalysis/getScoreAnalysis和/scoreAnalysis/getRankAndGpaByYear发送请求,从后端获取成绩分析所需的数据,使用ECharts图表库在gpaInit和gpaRankInit方法中初始化绩点折线图和绩点排名折线图,同时前端downloadPDF方法利用html2canvas和jsPDF库将成绩分析报告的HTML内容转换为PDF,并提供下载功能。

3.6 选课
学生可以在选课通知发布后进行课程选择,系统会显示可选课程列表,学生根据个人计划进行选课,学生可以在前端页面通过输入课程码并点击搜索按钮,触发事件调用后端/stu/room/getRoomByKey进行课程查询;查询结果返回后,前端展示并更新roomListPage.records,学生选择相应课程并点击“+”,此时前端再次调用后端/stu/room/bindRoom发送加入课堂的请求;后端验证学生权限和课程状态后,处理选课逻辑并更新数据库,最后将操作结果返回给前端,前端根据响应结果给学生展示相应的成功或错误提示。

回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-11-15 10:24:23 | 显示全部楼层
本帖最后由 08060402宋子怡 于 2024-11-15 10:31 编辑

本周我实现教师端的功能设计和实现
1.教师模块需求分析
教师可以录入学生成绩信息,录入方式可以是单个录入和批量录入。教师端还需要为教师提供课程管理,教师在课程管理中可以为专业绑定课程,在绑定课程之后,教师可以特定的学年,为相关专业创建指定的课堂, 该课堂课会在选课发布之后可以供给学生选课,同时对于学生提交的成绩复核申请还需要做出审核,同意的话就需要上传试卷材料,并设置更改分数。
2.教师模块功能概述
(1)绑定专业课程:教师可以将自己的课程与专业绑定,以便于教师后续创建课堂。
(2)审核成绩复核申请:教师可以审核学生提出的成绩复核申请,上传试卷材料,成绩更改信息以及添加备注,提交给管理员进行最终审核。
(3)课堂管理:教师可以创建对应专业的课堂并生成课堂码,课堂信息包括所属学年、所属专业、班级描述、所属课程、课程图片等。
教师还可以设定课程的上课时间表,包括现在开课、定时开课以及结束课程。
在特殊情况下,教师可以对学生进行强制退课处理。
在课堂管理中教师可以单个录入和修改学生的成绩,也可以通过Excel学生成绩模版批量录入和修改学生的成绩。
3.教师端功能实现
3.1  绑定专业课程
教师可以将自己的课程与专业绑定如图5.7所示,以便于教师后续创建课堂,教师在选项卡中选择不同的专业,触发 changeMajor 方法,该方法更新当前选择的专业并重新获取该专业的课程列表。教师在 el-transfer组件中可以选择要绑定的课程(从“可选课程”中转移到“已绑课程”)。教师点击“保存”按钮,触发 saveData 方法。在该方法中,向后端发送 POST 请求 (/cou/bindCourse)以提交绑定的课程信息。请求体包含当前选择的课程 ID 列表(couIds)和专业名称(major)后端接收到请求后,更新数据库中相应的课程绑定状态,并返回操作结果。前端根据后端返回的结果,更新界面显示或提示用户操作成功与否,完成整个用户交互流程。

图5.7 绑定课程
3.2 课堂管理
(1)创建课堂:教师可以创建对应专业的课堂并生成课堂码,创建课堂时,需要确定的课堂信息couFormData包括所属学年、所属专业、班级描述、所属课程、课程图片等如图5.8所示,点击“确定”按钮,前端调用 createRoom 方法,将 couFormData 数据通过 POST 请求发送到后端 /cou/createRoom 接口,后端通过VoRoom对象接受课堂信息,(StUser) SecurityUtils.getSubject().getPrincipal()获取当前用户的身份信息,使用RandomUtil.randomString(5).toUpperCase()生成课堂码,创建一个课堂信息存储到数据库,处理后返回相应的信息,前端获得成功响应后,显示创建成功消息,并调用 getTeaRoom 方法刷新课堂列表,确保界面显示最新的课堂信息。

图5.8 创建课堂表单
(2)设置课堂开课时间:教师还可以设定课程的上课时间表,包括现在开课、定时开课以及结束课程,教师选择好时间后如图5.9所示,点击“确定”按钮,前端会调用timingStart方法,将选择的时间通过POST请求发送到后端的/cou/timingStart接口,后端创建一个Job对象,设置其延迟时间为开课时间与当前系统时间的差值。设置Job的ID为roomId,主题为"test"。调用jobService.addDefJob(job)将这个定时任务添加到任务队列中。并调用classRoomService.updateById(classRoom)更新数据库中的课堂信息,包括设置开课时间为time。如果更新成功,返回成功的Result对象。如果更新失败,返回失败的Result对象,并附带错误信息“定时失败”。


图5.9 定时开课
(4)强制退课:教师可以在学生列表中选择某个学生,点击“强制退课”按钮。点击按钮后,前端调用exitRoomByTea方法,将roomId和stuId通过POST请求发送到后端的/cou/exitRoomByTea接口。端接收到请求后,根据提供的roomId和stuId参数,更新数据库中对应学生的课堂状态,将其从课堂中移除。操作成功后,后端返回成功响应,前端根据响应结果更新学生列表,并提示教师操作成功。
(3)赋予学生成绩:教师可以点击学生列表中某个学生的“给予成绩”按钮,打开一个弹出框,在其中输入学生的平时成绩和考试成绩。前端根据输入的成绩和课程设置的成绩占比,计算总成绩,并显示给教师。教师确认成绩无误后,点击“确定”按钮,前端调用updateGrade方法,将总成绩、roomId和stuId通过POST请求发送到后端的/cou/updateGrade接口。后端接收到请求后,根据提供的roomId、stuId和grade参数,更新数据库中对应学生的成绩。操作成功后,后端返回成功响应,前端根据响应结果更新学生的成绩,并提示教师操作成功。

图5.10 赋予学生成绩
4.部分实现代码如下:
public class TeaRoomController {

    @Autowired
    private CourseService courseService;
    @Autowired
    private CouTeaService couTeaService;

    @GetMapping("/labelInit")
    public Result getLabelInit(){
        return courseService.labelInit();
    }

    @GetMapping("/getTeaRoom")
    public Result getTeaRoom(String stuYear,String major){
        return courseService.getTeaRoom(stuYear,major);
    }

    @GetMapping("/getCourse")
    public Result getCourse(String major){
        return courseService.getCourse(major);
    }

    @GetMapping("/getMajor")
    public Result getMajor(){
        return courseService.getMajor();
    }

    @PostMapping("/bindCourse")
    public Result bindCourse(@RequestBody JSONObject couIds){
        return courseService.bindCourse(couIds.getJSONArray("couIds"),couIds.getString("major"));
    }

    @PostMapping("/upBcImg")
    public Result upBcImg(MultipartFile file,Long roomId){
        return courseService.upBcImg(file,roomId);
    }

    @PostMapping("/createRoom")
    public Result createRoom(@RequestBody VoRoom room){
        return courseService.createRoom(room);
    }

    @PostMapping("/changeState")
    public Result changeState(@RequestBody JSONObject jsonObject){
        return courseService.changeState(jsonObject.getIntValue("state"), jsonObject.getLong("roomId"));
    }

    @PostMapping("/timingStart")
    public Result timingStart(@RequestBody JSONObject jsonObject) throws ParseException {
        return courseService.timingStart(jsonObject.getString("time"), jsonObject.getLong("roomId"));
    }

    @PostMapping("/updateGrade")
    public Result updateGrade(@RequestBody JSONObject jsonObject){
        return couTeaService.updateGrade(jsonObject.getIntValue("grade"),
                jsonObject.getLong("roomId"),
                jsonObject.getLong("stuId"));
    }

    @PostMapping("/exitRoomByTea")
    public Result exitRoom(@RequestBody JSONObject jsonObject){
        return couTeaService.exitRoom(jsonObject.getLong("roomId"),jsonObject.getLong("stuId"),jsonObject.getIntValue("couType"));
    }

    @PostMapping("/upGrade")
    public Result upGrade(MultipartFile file,Long roomId) throws IOException {
        return couTeaService.upGrade(file,roomId);
    }

}//教师独有  {    path: '/courseBind',    component: Layout,    meta: {      title: '课程管理',      icon: '#icon-kechengxinxi',      roles: ['teacher']    },    children: [      {        path: 'couAdd',        name: 'couAdd',        component: () => import('@/views/course/couAdd'),        meta: {          title: '绑定课程',          icon: '#icon-zizhuxuanke',          roles: ['teacher']        }      }    ]  },  //成绩复核  {    path: '/reviewGrade',    component: Layout,    children: [      {        path: 'reviewList',        name: 'reviewList',        component: () => import('@/views/reviewGrade/reviewList'),        meta: {          title: '成绩复核列表',          icon: '#icon-zhuangxiangfuhe',          roles: ['teacher','admin']        }      },      {        hidden: true,        path: 'reviewDetail',        name: 'reviewDetail',        component: () => import('@/views/reviewGrade/reviewDetail'),        meta: {          title: '成绩复核详情',          roles: ['teacher','admin','student']        }      }    ]  },  //课堂管理  {    path: '/classRoom',    component: Layout,    meta: {      title: '课堂管理',      icon: '#icon-ketang2',      roles: ['student','teacher']    },    children: [      {        path: 'roomListTea',        name: 'roomListTea',        component: () => import('@/views/classRoom/roomListTea'),        meta: {          title: '我的课堂',          icon: '#icon-ketang1',          roles: ['teacher']        }      },      {        path: 'roomListStu',        name: 'roomListStu',        component: () => import('@/views/classRoom/roomListStu'),        meta: {          title: '我的课堂',          icon: '#icon-ketang1',          roles: ['student']        }      },      {        hidden:true,        path: 'roomDetail',        name: 'roomDetail',        component: () => import('@/views/classRoom/roomDetail'),        meta: {          roles: ['teacher','student']        }      },      {        path: 'roomAdd',        name: 'roomAdd',        component: () => import('@/views/course/bingCourse'),        meta: {          title: '加入课堂',          icon: '#icon-ketang',          roles: ['student']        }      }    ]  },  // 404 page must be placed at the end !!!  { path: '*', redirect: '/404', hidden: true }]

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-11-19 13:28:24 | 显示全部楼层
本帖最后由 08060402宋子怡 于 2024-11-19 13:29 编辑

这几天我完成管理员对用户的管理设计和实现
1. 设计思路:
管理员在学生管理和教师信息的学生信息中查看所有对应用户信息,实现查找用户、添加用户信息、编辑用户信息以及删除用户信息功能,前端使用Element UI组件库中的el-table展示学生和教师信息列表,el-pagination进行分页处理,当搜索用户,真实姓名采用模糊查询,用户名则是使用用户id进行精准查询,当点击“新增”和“编辑”按钮时触发显示el-dialog弹出框,在弹出框中编辑好用户信息点击“确定”,则向后端相应的接口专题表单数据,后端进行业务处理,如保存或更新学生updateStu(VoUser voUser)或教师信息updateTea(StUser stUser)到数据库。点击“删除”时触发触发delTea方法,通过传入当前用户ID实现删除教师或者学生信息,处理完业务逻辑后,后端返回操作结果成功return Result.succ(null)或失败return Result.fail("操作失败,存在重复信息")的状态码和消息。
2.后端实现
2.1 获得就角色列表,根据用户所属的role_id来区分角色是教师还是学生
public Result getUserList(int current,long userType,StUser stUser){
    IPage<StUser> page=new Page<>(current,pageSize);
    IPage<StUser> temPage = null;
    QueryWrapper<StUser> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("role_id", userType); // 设置角色ID条件
if(stUser!=null){
        stUser.setRoleId(userType);
        // 如果stUserrealName不为空,则也添加模糊查询条件
if (stUser.getRealName() != null && !stUser.getRealName().trim().isEmpty()) {
            queryWrapper.like("real_name", stUser.getRealName().trim());
        }

        // 如果stUser不为空,但usernamerealName都为空,则只根据role_id查询
// 由于role_id已经在queryWrapper中设置,所以这里不需要额外操作
// 执行分页查询
temPage = userService.page(page, queryWrapper);
    }else {
        temPage = userService.page(page, new QueryWrapper<StUser>().eq("role_id", userType));
    }
    if(userType==1&&!CollectionUtils.isEmpty(temPage.getRecords())){
        return Result.succ(convertToVoUser(temPage));
    }
    return Result.succ(temPage);
}2.2 更新用户信息
定义了两个方法,updateTea和updateStu,用于更新教师和学生信息。
updateTea 方法,这个方法接收一个StUser类型的参数stUser,用于更新教师信息。首先调用isRepeat(stUser)方法来检查是否存在重复信息。如果存在重复信息,则继续执行;如果不存在,则返回失败结果。如果stUser的id为null(即新用户),则调用initPassword(stUser)方法初始化密码。赋予教师角色:通过roleService.getOne方法查询角色为“teacher”的角色ID,并将该ID设置为stUser的roleId。调用userService.saveOrUpdate(stUser)方法保存或更新用户信息。updateStu 方法
这个方法接收一个VoUser类型的参数voUser,用于更新学生信息。创建一个新的StUser对象,并通过BeanUtils.copyProperties方法将voUser的属性复制到stUser中。如果voUser的majorClass属性不为空且第一个元素不为空,则将专业班级ID和专业ID设置到stUser中。如果voUser的dorm属性不为空且第二个元素不是“待完善”,则将宿舍ID设置到stUser中。调用isRepeat(stUser)方法来检查是否存在重复信息。如果存在重复信息,则继续执行;如果不存在,则返回失败结果。如果stUser的id为null(即新用户),则调用initPassword(stUser)方法初始化密码。通过roleService.getOne方法查询角色为“student”的角色ID,并将该ID设置为stUser的roleId。调用userService.saveOrUpdate(stUser)方法保存或更新用户信息。
public Result updateTea(StUser stUser) {
    if(isRepeat(stUser)){
        if(stUser.getId()==null){
            initPassword(stUser);
        }
        //赋予教师角色
Long roleId= roleService.getOne(new QueryWrapper<StRole>().eq("role","teacher")).getId();
        stUser.setRoleId(roleId);
        userService.saveOrUpdate(stUser);
        return Result.succ(null);
    }else {
        return Result.fail("操作失败,存在重复信息");
    }
}

public Result updateStu(VoUser voUser){
    StUser stUser = new StUser();
    //对象转换
BeanUtils.copyProperties(voUser,stUser);
    if(voUser.getMajorClass()!=null&&voUser.getMajorClass()[0]!=null){
        stUser.setMajorClassId(Long.valueOf(voUser.getMajorClass()[1].toString()));
        stUser.setMajorId((Long.valueOf(voUser.getMajorClass()[0].toString())));
    }
    if(voUser.getDorm()!=null&&!voUser.getDorm()[1].equals("待完善")){
        stUser.setDormId((Long.valueOf(voUser.getDorm()[1].toString())));
    }
    if(isRepeat(stUser)){
        if(stUser.getId()==null){
            initPassword(stUser);
        }
        //赋予学生角色
Long roleId= roleService.getOne(new QueryWrapper<StRole>().eq("role","student")).getId();
        stUser.setRoleId(roleId);
        userService.saveOrUpdate(stUser);
        return Result.succ(null);
    }else {
        return Result.fail("操作失败,存在重复信息");
    }
}3.前端设计利用element-ui提供的表单模板可实现管理操作
回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-11-22 09:16:52 | 显示全部楼层
近两天我完成对管理员模块的专业信息管理以及选课公告发布
1. 功能需求
(1)管理员可以添加新的专业。为专业添加课程,包括课程名称、学分、学时、所属专业、学时比、课程类别(通识教育、专业基础课、专业核心课、专业实践课)、课程属性(必修、选修)。
对于专业课程,管理员可以编辑和删除专业课程信息。同时管理员可以为专业添加新的班级,专业班级的名称由年份和序号组成,管理员通过输入制定年份和数量,为当前专业创建指定年份和固定数量的专业班级。(2)发布选课公告:管理员可以管理和发布选课相关的公告,包括选课时间、选课对象、注意事项等。


2.功能设计
2.1 数据库字段st_course

st_major

st_class

st_bing_course

2.2 功能实现
2.2.1专业课程
管理专业信息包括添加专业课程、编辑专业课程、删除专业课程以及添加专业课程功能。
(1)添加专业课程:在所选专业下,管理员可以点击“添加课程”按钮打开课程编辑弹出框,输入课程信息后调用 saveOrUpdateCourse 方法。该方法会将课程数据couData通过POST请求发送到 /major/saveOrUpdateCourse接口,后端通过majorCourseService.addCourse(stCourse)来插入新课程还是更新已有课程,并返回结果,前端则更新课程表显示最新信息。
(2)编辑专业课程:管理员通过点击“编辑”按钮打开弹出框,更改课程名称、学分、学时比、课程类别以及课程属性,点击“确定”,触发saveOrUpdateCourse方法,该方法将表单数据通过接口将新专业名称发送到后端。后端更新数据库并返回相关信息。
(3)删除专业课程:前端通过delCourse和delMajor方法向后端/major/delCourse接口发生POST请求来删除专业信息或者删除专业下面的课程。成功删除后,更新专业数据并通知用户。需要注意的是再删除专业课程时候,后端需要查询是否该课程有绑定的学生,如果有绑定的的学生则返回失败信息Result.fail("该课程下仍有绑定的学生")。成功删除后,前端将更新课程列表。
(4)添加专业班级:管理员可在每个专业下点击“快速添加专业班级”按钮,输入年份和班级数量,调用 addMajorClass 方法,该方法将数据发送到/major/addMajorClass接口进行处理,后端使用StMajorClass对象接受数据,通过循环迭代构建专业班级,专业名+年份+序号。序号使用 String.format 格式化为两位数,并通过classService 检查该班级名称在数据库中是否已存在,如果存在,打印信息并跳过该班级的添加。如果班级不存在,则构造 StClass 对象并调用 classService.save 方法保存新班级到数据库后端成功处理后返回处理的消息。

2.2.2 发布选课公告
管理员通过前端页面来获取、添加和删除选课任务。具体实现流程为:在组件创建时,前端调用 getBingCourseList 方法,发送 GET 请求到后端/admin/getBingCourseList获取选课任务列表并更新界面;管理员在表单中填写新增选课任务的信息(选课时间、选课对象、注意事项)后,前端通过 POST 请求将数据提交到后端的/admin/addBingCourse接口,后端进行校验并保存数据;若保存成功,前端接收成功消息并刷新任务列表;当管理员删除选课任务时,前端发送 POST 请求到后端的/admin/delBingCourse方法,后端执行删除操作并返回结果,前端再次更新界面以反映最新的选课任务状态。
前端代码:



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

1

主题

7

帖子

33

积分

新手上路

Rank: 1

积分
33
 楼主| 发表于 2024-11-29 09:19:14 | 显示全部楼层
本周我完善和设计管理员功能的其他部分:
完善部分:发布选课公告,管理员可以管理和发布选课相关的公告,包括选课时间、选课对象、注意事项等。
审批成绩复核申请:管理员可以查看所有成绩复核申请,包括审核和批复,当管理员同意该成绩复核,则代表整个成绩复核流程结束。
1. 分析
(1)发布选课公告需要设置定时任务,例如选课时间段,在设置选课之后,我们需要注意的是要确保选课信息发布后,教师端能够进行课程绑定,并在学生端进行显示,同时,学生选课应该注意同意学年的课程不能同时进行选课和重修同时进行。
(2)审批成绩复核申请,管理员对学生提交的有问题成绩的复核进行最终的审批,审批通过后在管理员和学生端要及时更新状态,同时学生端还需要更新相关的成绩信息。
2. 设计实现
2.1 发布选课任务:
定时任务实现:
2.2审批成绩复核申请
学生通过点击“我的课堂”,找到有疑问的课堂成绩,点击“申请成绩复核”,输入相关备注,点击“确定”后触发createReviewByStu方法被调用,后端创建一个新的StReviewGrade对象,记录学生的申请信息(包括学生ID、课堂ID、学生备注、原始成绩和申请日期),并将状态设置为“学生申请完毕”,然后保存这个对象到数据库。学生同时查看申请进度。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

游客
回复
您需要登录后才可以回帖 登录 | 立即注册

Archiver|手机版|小黑屋|ekn

GMT+8, 2025-7-12 21:30 , Processed in 0.047954 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表