毕业设计实战:基于Spring Boot的银行OA系统全栈开发

37 阅读18分钟

一、项目背景:数字化时代的银行办公革新

在金融行业竞争日益激烈的当下,银行办公效率与管理规范化成为核心竞争力。截至2024年,国内中小银行数量超4000家,但超60%仍依赖纸质文档、Excel表格等传统方式处理办公事务,存在“流程繁琐、信息孤岛、协作低效”等痛点——员工请假需线下签字审批,任务分配依赖口头传达,员工档案查询耗时久,严重制约银行办公效率与数字化转型进程。

在此背景下,基于Spring Boot的银行OA系统成为破解银行办公难题的关键解决方案。系统采用B/S架构,通过信息化手段实现员工管理、任务分配、请假审批、会议申请等办公流程的全线上化,构建“管理员统筹管控-经理层级审核-员工高效执行”的三级协同机制。本毕业设计以银行实际办公需求为导向,为中小银行提供轻量化、低成本的办公自动化方案,助力银行摆脱传统办公束缚,实现“无纸化、高效化、规范化”办公转型。

二、技术架构:银行OA系统的全栈技术选型

项目以“安全性、稳定性、易用性”为核心设计原则,选用业界成熟的Java Web技术栈,确保系统在敏感数据存储、多角色权限管控等场景下的可靠运行,具体技术选型如下:

技术模块具体工具/技术核心作用
后端框架Spring Boot 2.x简化配置流程,快速构建分层架构(Controller/Service/DAO),支持事务管理与依赖注入,适配银行复杂业务逻辑
数据库MySQL 8.0存储员工信息、部门数据、审批记录、任务详情等核心敏感数据,支持多表关联查询,保障数据一致性与安全性
前端技术H5 + CSS3 + JavaScript + Bootstrap构建响应式办公界面,适配PC端设备,实现表单验证、异步审批、数据可视化展示等交互功能
架构模式B/S结构无需安装客户端,员工通过浏览器即可访问,降低使用门槛;仅需维护服务器端,减少银行IT运维成本
开发工具MyEclipse + NavicatMyEclipse用于Java代码开发与调试,Navicat实现MySQL数据库可视化管理(表设计、数据备份、权限控制)
服务器Tomcat 8.0部署Web应用,处理HTTP请求,支撑多用户同时在线办公,保障系统稳定运行
安全机制密码MD5加密 + 基于角色的权限控制(RBAC)对员工密码进行不可逆加密存储,通过“管理员-经理-员工”三级角色隔离功能权限,防止越权访问敏感数据

三、项目全流程:6步完成银行OA系统开发

3.1 第一步:需求分析——明确系统核心价值

针对银行传统办公的“流程割裂、效率低下、管理混乱”痛点,本系统聚焦“流程闭环、权限可控、数据可溯”,核心需求分为功能性与非功能性两类:

3.1.1 功能性需求

  1. 三级角色权限体系

    • 管理员:系统初始化配置(部门/角色创建)、员工全生命周期管理(新增/编辑/离职注销)、审批流程配置(请假/出差/报销审批节点设置)、系统数据备份与日志管理;
    • 经理:下属员工任务分配与进度跟踪、部门内请假/出差/报销申请审核、部门会议组织与记录、下属工作日常查看;
    • 员工:个人信息维护(手机号/邮箱更新)、请假/出差/报销/会议申请提交、工作任务接收与反馈、个人工作日常记录与查询。
  2. 核心办公功能

    • 员工管理:员工档案(工号、部门、职位)维护、离职员工数据归档、员工权限角色分配;
    • 审批管理:请假(事假/病假/年假)、出差、报销、会议申请的提交→审核→反馈全流程线上化;
    • 任务管理:管理员/经理发布任务(含截止时间、优先级)、员工接收任务并更新进度、任务完成后归档;
    • 日常办公:员工记录每日工作内容、查看部门会议安排、查询个人审批历史记录。

3.1.2 非功能性需求

  • 系统安全性:敏感数据(员工身份证、薪酬信息)加密存储、操作日志全程记录(谁在何时操作了哪项数据)、登录失败5次后账号锁定;
  • 响应及时性:页面加载时间≤1.5秒,审批申请提交后实时推送通知给审核人,无明显延迟;
  • 数据稳定性:核心数据(审批记录、员工档案)每日自动备份,支持30天内数据恢复,防止数据丢失;
  • 兼容性:支持Chrome、Edge、Firefox等主流浏览器,界面布局无错乱,功能正常使用。

3.2 第二步:系统设计——构建整体架构

采用经典三层架构(表现层/业务逻辑层/数据访问层)实现“解耦”,同时设计严格的数据库结构与权限控制逻辑,保障银行数据安全:

3.2.1 系统总体架构

  1. 表现层(Web层)

    • 界面展示:基于H5+Bootstrap构建三级角色专属界面(管理员后台、经理工作台、员工办公页),包含表单、列表、审批流进度条等组件;
    • 交互控制:通过JavaScript实现表单验证(如请假时长合理性校验、手机号格式校验)、异步请求(如实时加载审批进度)、弹窗通知(审核结果推送)。
  2. 业务逻辑层(Service层)

    • 核心服务:用户认证服务(登录/权限校验)、员工管理服务(档案维护)、审批流服务(申请提交/审核)、任务管理服务(任务分配/进度跟踪);
    • 规则控制:审批流状态流转(待审核→审核通过/驳回)、任务优先级排序(高/中/低)、员工离职后数据归属(归档至历史表)。
  3. 数据访问层(DAO层)

    • 数据操作:通过MyBatis封装SQL语句,实现数据库CRUD操作(如查询员工审批记录、更新任务进度);
    • 事务管理:确保关键业务(如审批通过后同步更新员工考勤状态)的数据一致性,避免部分操作成功、部分失败的情况。

3.2.2 核心数据库设计

系统设计9张核心数据表,覆盖三级角色办公全场景,关键表结构如下:

表名核心字段作用
department(部门表)depid(部门ID)、depname(部门名称)、depp_id(父部门ID)存储银行部门层级结构,支持多级部门管理
employee(员工信息表)userid(员工ID)、userghao(工号)、usernamers(姓名)、userorg_id(所属部门ID)、userloginpw(加密密码)、shouji(手机号)存储员工基本信息,支撑登录与身份识别
admin(管理员表)userida(管理员ID)、usernamea(账号)、userPwa(加密密码)存储管理员账号信息,控制系统最高权限
leave_apply(请假申请表)idrs(申请ID)、unameid(申请人ID)、leave_type(请假类型)、start_time(开始时间)、end_time(结束时间)、zt(审批状态)、shhf(审核回复)记录员工请假申请与审核结果
task(任务表)idtz(任务ID)、nametz(任务名称)、contz(任务内容)、riqitz(截止时间)、priority(优先级)、assigner_id(分配人ID)、receiver_id(接收人ID)存储任务分配与进度数据
daily_work(工作日常表)id(记录ID)、title(标题)、riqi(日期)、user_id(员工ID)、con(工作内容)记录员工每日工作内容,支持后续查询
meeting(会议表)wdid(会议ID)、riqi(会议日期)、title(会议主题)、location(会议地点)、participant_ids(参会人ID列表)存储部门会议安排,通知参会人员

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现三级角色核心业务逻辑,重点解决“权限管控”“审批流处理”“任务分配”三大核心问题,关键代码如下:

3.3.1 三级角色权限控制实现

@Service
public class AuthService {
    
    @Autowired
    private AdminMapper adminMapper;
    
    @Autowired
    private EmployeeMapper employeeMapper;
    
    @Autowired
    private ManagerMapper managerMapper;
    
    /**
     * 统一登录验证(区分管理员/经理/员工)
     */
    public LoginResult login(String account, String password, String role) {
        // 1. 密码MD5加密(与数据库存储的加密密码比对)
        String encryptedPwd = DigestUtils.md5DigestAsHex(password.getBytes());
        
        // 2. 按角色校验账号密码
        switch (role) {
            case "admin":
                Admin admin = adminMapper.selectByAccountAndPwd(account, encryptedPwd);
                if (admin != null) {
                    return new LoginResult(true, "admin", admin.getUserida().toString(), "管理员");
                }
                break;
            case "manager":
                Manager manager = managerMapper.selectByAccountAndPwd(account, encryptedPwd);
                if (manager != null) {
                    return new LoginResult(true, "manager", manager.getManagerId().toString(), manager.getManagerName());
                }
                break;
            case "employee":
                Employee employee = employeeMapper.selectByJobNumAndPwd(account, encryptedPwd);
                if (employee != null) {
                    return new LoginResult(true, "employee", employee.getUserid().toString(), employee.getUsernamers());
                }
                break;
            default:
                return new LoginResult(false, null, null, null);
        }
        
        // 3. 登录失败(账号密码错误或角色不匹配)
        return new LoginResult(false, null, null, null);
    }
    
    /**
     * 权限校验(防止越权访问,如员工访问管理员后台)
     */
    public boolean checkPermission(String userId, String role) {
        switch (role) {
            case "admin":
                return adminMapper.selectById(Long.parseLong(userId)) != null;
            case "manager":
                return managerMapper.selectById(Long.parseLong(userId)) != null;
            case "employee":
                return employeeMapper.selectById(Long.parseLong(userId)) != null;
            default:
                return false;
        }
    }
    
    /**
     * 记录操作日志(谁在何时操作了哪项功能)
     */
    public void recordOperLog(String userId, String role, String operContent) {
        OperLog log = new OperLog();
        log.setUserId(userId);
        log.setUserRole(role);
        log.setOperContent(operContent);
        log.setOperTime(new Date());
        operLogMapper.insert(log);
    }
}

3.3.2 请假审批流功能实现

@RestController
@RequestMapping("/api/approval/leave")
public class LeaveApprovalController {
    
    @Autowired
    private LeaveApprovalService leaveService;
    
    @Autowired
    private AuthService authService;
    
    /**
     * 员工提交请假申请
     */
    @PostMapping("/submit")
    public ResponseEntity<?> submitLeave(@RequestBody LeaveSubmitDTO submitDTO, HttpSession session) {
        try {
            // 1. 从Session获取当前登录员工ID(防止伪造请求)
            String employeeId = (String) session.getAttribute("userId");
            if (employeeId == null || !authService.checkPermission(employeeId, "employee")) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body("未登录或无员工权限");
            }
            
            // 2. 验证请假时长合理性(如年假最多15天,事假单次不超过3天)
            long leaveDays = calculateLeaveDays(submitDTO.getStartTime(), submitDTO.getEndTime());
            if ("年假".equals(submitDTO.getLeaveType()) && leaveDays > 15) {
                return ResponseEntity.badRequest().body("年假单次最长15天");
            }
            if ("事假".equals(submitDTO.getLeaveType()) && leaveDays > 3) {
                return ResponseEntity.badRequest().body("事假单次最长3天");
            }
            
            // 3. 封装请假申请数据
            LeaveApply leaveApply = new LeaveApply();
            leaveApply.setUnameid(Long.parseLong(employeeId));
            leaveApply.setLeaveType(submitDTO.getLeaveType());
            leaveApply.setStartTime(submitDTO.getStartTime());
            leaveApply.setEndTime(submitDTO.getEndTime());
            leaveApply.setLeaveReason(submitDTO.getLeaveReason());
            leaveApply.setZt("待审核"); // 初始状态:待经理审核
            leaveApply.setApplyTime(new Date());
            
            // 4. 保存申请并推送通知给部门经理
            leaveService.saveLeaveApply(leaveApply);
            leaveService.pushNoticeToManager(leaveApply); // 通知经理有新申请待审核
            
            // 5. 记录操作日志
            authService.recordOperLog(employeeId, "employee", "提交请假申请,申请ID:" + leaveApply.getIdrs());
            
            return ResponseEntity.ok("请假申请提交成功,等待部门经理审核");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.internalServerError().body("提交失败:" + e.getMessage());
        }
    }
    
    /**
     * 经理审核请假申请
     */
    @PostMapping("/audit")
    public ResponseEntity<?> auditLeave(@RequestBody LeaveAuditDTO auditDTO, HttpSession session) {
        try {
            // 1. 验证经理权限
            String managerId = (String) session.getAttribute("userId");
            if (managerId == null || !authService.checkPermission(managerId, "manager")) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body("未登录或无经理权限");
            }
            
            // 2. 查询请假申请是否存在
            LeaveApply leaveApply = leaveService.getLeaveApplyById(auditDTO.getApplyId());
            if (leaveApply == null) {
                return ResponseEntity.badRequest().body("请假申请不存在");
            }
            
            // 3. 验证申请状态(仅“待审核”可操作)
            if (!"待审核".equals(leaveApply.getZt())) {
                return ResponseEntity.badRequest().body("申请已审核,无需重复操作");
            }
            
            // 4. 更新审核状态与回复
            leaveApply.setZt(auditDTO.getAuditResult()); // 通过/驳回
            leaveApply.setShhf(auditDTO.getAuditReply());
            leaveApply.setAuditTime(new Date());
            leaveApply.setAuditorId(Long.parseLong(managerId));
            leaveService.updateLeaveApply(leaveApply);
            
            // 5. 推送审核结果给申请人
            leaveService.pushAuditResultToEmployee(leaveApply);
            
            // 6. 记录操作日志
            authService.recordOperLog(managerId, "manager", "审核请假申请,申请ID:" + auditDTO.getApplyId() + ",结果:" + auditDTO.getAuditResult());
            
            return ResponseEntity.ok("审核完成,结果已通知申请人");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.internalServerError().body("审核失败:" + e.getMessage());
        }
    }
    
    /**
     * 辅助方法:计算请假天数
     */
    private long calculateLeaveDays(Date startTime, Date endTime) {
        long diff = endTime.getTime() - startTime.getTime();
        return diff / (1000 * 60 * 60 * 24) + 1; // 包含开始和结束日期
    }
}

3.3.3 任务分配与进度跟踪实现

@Service
public class TaskService {
    
    @Autowired
    private TaskMapper taskMapper;
    
    @Autowired
    private EmployeeMapper employeeMapper;
    
    /**
     * 管理员/经理发布任务
     */
    public Task publishTask(TaskPublishDTO publishDTO, String publisherId, String publisherRole) {
        // 1. 验证接收人是否为本部门员工(经理只能给下属分配任务)
        Employee receiver = employeeMapper.selectById(publishDTO.getReceiverId());
        if (receiver == null) {
            throw new RuntimeException("任务接收人不存在");
        }
        if ("manager".equals(publisherRole)) {
            Manager manager = managerMapper.selectById(Long.parseLong(publisherId));
            if (!receiver.getUserorg_id().equals(manager.getDeptId())) {
                throw new RuntimeException("仅能给本部门员工分配任务");
            }
        }
        
        // 2. 封装任务数据
        Task task = new Task();
        task.setNametz(publishDTO.getTaskName());
        task.setContz(publishDTO.getTaskContent());
        task.setRiqitz(publishDTO.getDeadline());
        task.setPriority(publishDTO.getPriority()); // 高/中/低
        task.setAssignerId(Long.parseLong(publisherId));
        task.setReceiverId(publishDTO.getReceiverId());
        task.setTaskStatus("未开始"); // 初始状态:未开始
        task.setPublishTime(new Date());
        
        // 3. 保存任务并通知接收人
        taskMapper.insertTask(task);
        pushTaskNoticeToReceiver(task); // 通知员工有新任务
        
        return task;
    }
    
    /**
     * 员工更新任务进度
     */
    public void updateTaskProgress(Long taskId, String progress, String employeeId) {
        // 1. 验证任务归属
        Task task = taskMapper.selectById(taskId);
        if (task == null) {
            throw new RuntimeException("任务不存在");
        }
        if (!task.getReceiverId().toString().equals(employeeId)) {
            throw new RuntimeException("无权限更新他人任务");
        }
        
        // 2. 更新进度(未开始→进行中→已完成)
        List<String> validProgress = Arrays.asList("未开始", "进行中", "已完成");
        if (!validProgress.contains(progress)) {
            throw new RuntimeException("进度状态仅支持:未开始、进行中、已完成");
        }
        task.setTaskStatus(progress);
        task.setUpdateTime(new Date());
        taskMapper.updateTask(task);
        
        // 3. 若任务完成,通知分配人
        if ("已完成".equals(progress)) {
            pushTaskCompleteNoticeToAssigner(task);
        }
    }
    
    /**
     * 查询员工待办任务
     */
    public List<Task> getPendingTasks(String employeeId) {
        return taskMapper.selectByReceiverIdAndStatus(
                Long.parseLong(employeeId), 
                Arrays.asList("未开始", "进行中")
        );
    }
}

3.4 第四步:前端界面实现——三级角色适配界面

基于H5+Bootstrap构建管理员、经理、员工专属界面,确保操作逻辑符合银行办公习惯,核心界面功能如下:

3.4.1 管理员界面

  • 系统首页:数据概览(员工总数、待审核申请数、今日任务数)、快速操作入口(新增员工、备份数据);
  • 员工管理页:员工列表(工号、姓名、部门、状态)、新增/编辑/注销按钮,支持按部门筛选;
  • 权限配置页:角色列表(管理员/经理/员工)、权限分配(勾选可操作的功能模块);
  • 日志管理页:操作日志列表(操作人、时间、内容),支持按时间范围查询。

3.4.2 经理界面

  • 工作台首页:待办事项(待审核申请、待分配任务)、部门员工任务完成率统计;
  • 审批管理页:待审核申请列表(申请人、申请类型、提交时间)、审核按钮(填写回复并选择通过/驳回);
  • 任务管理页:任务分配表单(选择接收人、设置截止时间)、部门任务进度列表;
  • 部门报表页:下属员工请假统计、任务完成率排行。

3.4.3 员工界面

  • 办公首页:待办任务(含截止时间提醒)、审批进度(申请当前状态)、今日会议通知;
  • 申请中心:请假/出差/报销/会议申请表单(带必填项校验)、历史申请记录查询;
  • 任务中心:待办/已办任务列表、更新进度按钮(选择“进行中”“已完成”);
  • 工作日常:工作记录表单(填写当日工作内容)、历史工作记录查询。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3.5 第五步:系统测试——确保安全可靠

通过多维度测试验证系统功能完整性与安全性,重点覆盖权限管控、审批流、数据安全等核心场景:

3.5.1 功能测试

设计测试用例覆盖三级角色关键操作,确保功能符合银行办公需求:

测试场景预期结果实际结果是否通过
员工提交请假申请申请状态为“待审核”,经理收到通知符合预期,通知实时推送
经理驳回请假申请员工收到驳回通知,申请状态更新为“驳回”符合预期,状态同步更新
管理员新增员工员工账号生成,默认密码发送至员工手机符合预期,账号正常登录
员工更新任务进度为“已完成”分配人收到完成通知,任务状态更新符合预期,通知准确推送
非管理员访问管理员后台被拦截并跳转至登录页符合预期,权限控制有效

3.5.2 非功能测试

  • 安全性测试:尝试输入SQL注入语句(如员工账号输入“' or 1=1 #”),系统过滤并提示非法输入;查看敏感数据(员工密码),数据库存储为MD5加密串,无法解密;
  • 性能测试:20名员工同时提交请假申请,平均响应时间1.1秒;50名员工同时查询任务列表,页面加载时间≤1.3秒;
  • 数据恢复测试:删除一条员工档案,通过备份数据恢复,3分钟内恢复成功,数据无丢失;
  • 兼容性测试:在Chrome 120、Edge 120、Firefox 119浏览器中测试,界面布局无错乱,功能正常使用。

3.6 第六步:问题排查与优化——提升办公体验

开发过程中遇到的典型问题及解决方案:

  1. 经理审核申请后员工未及时收到通知

    • 问题:审核结果依赖员工手动刷新页面查看,体验差;
    • 解决方案:集成WebSocket实时通信,审核完成后前端自动弹窗提示,无需手动刷新。
  2. 员工离职后任务数据归属混乱

    • 问题:员工离职后,其未完成任务无人接手,数据滞留;
    • 解决方案:在员工注销功能中添加“任务交接”步骤,管理员需指定新接收人后才能完成注销,确保任务不中断。
  3. 敏感数据(如薪酬)被普通员工查看

    • 问题:数据库表设计时未做权限隔离,普通员工可通过URL参数篡改查看他人数据;
    • 解决方案:前端界面隐藏敏感字段,后端接口添加数据权限校验(仅管理员/本人可查看敏感数据),同时数据库添加行级权限(RLS)。
  4. 系统登录后长时间无操作导致数据泄露

    • 问题:员工离开工位未退出系统,他人可操作其账号;
    • 解决方案:设置Session超时时间(30分钟无操作自动登出),并添加页面闲置检测,闲置10分钟后弹窗提醒。

四、毕业设计复盘:经验总结与实践建议

4.1 开发过程中的技术收获

  1. 权限管控实践:深入理解RBAC(基于角色的权限控制)模型,通过拦截器+数据库行级权限双重保障,解决银行敏感数据安全问题;
  2. 审批流设计思维:掌握“状态机”设计模式,通过枚举定义审批状态,确保流程流转不混乱(如“待审核”只能转为“通过”或“驳回”);
  3. 前后端协同效率:明确前后端数据交互规范(如请求参数用DTO封装、响应统一格式为{code:0,msg:"成功",data:{}}),减少沟通成本;
  4. 问题解决能力:面对实时通知、数据权限等复杂需求,学会通过技术文档(如Spring Boot官方文档、WebSocket协议文档)寻找解决方案。

4.2 给后续开发者的建议

  1. 优先保障数据安全:银行OA系统涉及大量敏感数据,开发初期需明确数据分级(公开/内部/敏感),敏感数据必须加密存储,操作日志全程记录;
  2. 贴合办公实际场景:调研银行真实办公流程(如请假审批是否需要HR二次审核),避免功能设计与实际脱节;
  3. 注重操作便捷性:银行员工日常办公繁忙,界面需简洁(常用功能放在首页)、操作步骤需精简(如申请表单自动填充部分信息);
  4. 预留扩展接口:设计时考虑未来需求(如对接银行薪酬系统、添加电子签章功能),数据库表预留冗余字段(如员工表添加“薪酬等级”字段)。

五、项目资源与发展展望

5.1 项目核心资源

本项目提供完整的开发与部署资料,便于后续学习与二次开发:

  • 源码资源:后端Spring Boot项目源码(含三级角色接口实现)、前端H5页面源码(含CSS/JavaScript资源);
  • 数据库资源:MySQL建表语句(含测试数据)、数据库ER图、数据备份脚本;
  • 部署文档:本地开发环境搭建指南(JDK、MySQL、Tomcat安装配置)、服务器部署步骤(Linux系统下Nginx+Tomcat配置);
  • 接口文档:三级角色RESTful接口说明(请求参数、响应格式、错误码)、权限控制规则说明。

5.2 系统扩展方向

  1. 功能扩展

    • 薪酬管理:对接银行薪酬系统,员工在线查看工资条,管理员在线发起薪酬调整;
    • 电子签章:审批文件(如请假单、报销单)支持电子签章,具备法律效力;
    • 移动端支持:开发微信小程序,员工可在手机端提交申请、查看任务,摆脱PC端限制;
    • 数据分析:基于员工办公数据(如任务完成率、请假频次)生成部门效率报表,为管理层决策提供支持。
  2. 技术升级

    • 前端重构:使用Vue.js+Element UI替代H5+JSP,实现单页应用(SPA),提升交互体验;
    • 后端优化:引入Spring Security实现更细粒度的权限控制(如某经理仅能查看本部门数据),使用Redis缓存高频访问数据(如员工基础信息);
    • 安全加固:集成OAuth2.0实现第三方登录(如银行统一认证系统),添加API接口限流(防止恶意请求)。
  3. 场景延伸

    • 多银行适配:支持多银行租户隔离(不同银行数据独立存储),提供个性化配置(如不同银行审批流程不同);
    • 合规管理:添加金融行业合规功能(如员工行为审计、敏感操作双人复核),满足监管要求;
    • 协同办公:增加团队协作模块(如共享文档、在线会议),支持跨部门协作。

本项目作为本科毕业设计,不仅实现了银行OA系统的三级角色核心功能,更完整覆盖了“需求分析-系统设计-编码实现-测试优化”的软件开发全流程。通过实战开发,既巩固了Java Web、数据库、权限管控等理论知识,也培养了符合金融行业需求的“安全第一、流程规范”思维,为后续从事企业级应用开发奠定了坚实基础。如果本文对您的Spring Boot学习或银行OA系统相关毕业设计有帮助,欢迎点赞+收藏+关注,后续将分享更多Java Web项目实战案例!