Spring Boot 2 + Vue 3 + MyBatis-Plus + MySQL 8.0办公管理系统实战解析

做Java Web开发的人应该都见过这类项目:办公管理系统。网上存量的项目资源非常多,但真正能用Spring Boot 2 + Vue 3 + MyBatis-Plus + MySQL 8.0这套组合把它们串起来的,其实不算多。大多数老项目要么停留在JSP时代,要么用Vue 2 + Element UI,要么数据库还是MySQL 5.7,换到新版环境时各种水土不服。这个标题里的项目,恰好踩在了我日常工作中最常用的一套技术栈上,而且自带文档,光这点就值得先说一句:这是少见的“拿到手能真正跑起来”的完整度。

如果你正好在学Spring Boot和Vue 3,或者公司想快速搭一套内部OA,又或者你手头有个课设/毕设要做,那这个项目的参考价值都不低。它解决的核心问题是:怎么用前后端分离的方式,把员工管理、部门管理、请假审批、公告通知、考勤统计这些常规办公场景落地成一套可维护的系统。下面我会从项目设计、技术选型、真实开发细节到排错经验,把这套东西完整拆一遍。

1. 项目整体设计与思路拆解

1.1 办公管理系统到底在管什么

很多人一听到“办公管理系统”就以为是个大杂烩,其实真正落到代码层面,它的业务边界是相当清晰的。我拆过不少这类项目,核心模块基本逃不开这几块:组织架构、人员信息、考勤管理、请假与出差审批、公告发布、会议管理、个人待办。这个项目也是一样的套路。

组织架构和人员信息是整个系统最底层的基础数据。部门表、岗位表、用户表,这三张表几乎决定了后续所有业务逻辑的走向。请假审批需要知道申请人的部门归属,考勤统计需要知道用户的状态和时间规则,公告发布需要知道接收对象的组织范围。所以这套系统在数据库设计上,用户表一定会带上部门ID和岗位ID,部门表则通常会设计成父子层级,方便做树形展示。

审批流是另一个关键模块。办公系统里最烦人的开发点往往就是审批,因为它的状态流转很绕。一个请假单从提交到部门主管审批,再到人事确认,中间要经历多个状态节点。大部分教学级项目不会直接上流程引擎,而是用一张“审批记录表”配合状态字段来模拟。这种实现方式的好处是简单直观、容易理解,坏处是流程一旦复杂就不好维护。这个项目的处理方式,属于前者里的典型做法:请假单表里存当前状态,另建一张审批记录表存每次操作的人和意见。

考勤模块则通常是“打卡数据 + 统计规则”。前端提供打卡按钮,后端记录打卡时间,然后按月份统计出勤天数、迟到次数、请假天数。这里最容易忽略的是时区问题和周末判断,很多项目上线后出现打卡时间偏移,基本都是这两个地方没处理好。

公告和会议模块相对简单,就是增删改查加一个已读状态。但需要注意,这类功能往往要跟待办提醒联动:登录首页要有“未读公告数”“待我审批的单据数”。这就要在首页聚合查询时,把多张表的统计结果合并到一个接口里返回,减少前端的请求次数。

1.2 技术栈选型背后的逻辑

项目为什么选Spring Boot 2而不是Spring Boot 3,选Vue 3而不是Vue 2,选MyBatis-Plus而不是MyBatis原生,背后其实都有非常务实的考量。

先说Spring Boot 2。虽然Spring Boot 3已经发布很久了,但国内很多企业的存量系统依然跑在Spring Boot 2.x上,特别是2.6和2.7这个大版本段。原因很现实:大量中间件和第三方开源库对Spring Boot 3的兼容性还在逐步跟进,而Spring Boot 2经过多年沉淀,生态稳定、资料齐全、坑基本都被踩平了。对于教学项目和中小型企业内部系统来说,稳定压倒一切,选Spring Boot 2不是落后,是求稳。

Vue 3就更不用犹豫。Vue 2已经停止维护,组合式API带来的代码组织方式,在应对办公系统这种“组件多、状态交互频繁”的场景时,比选项式API舒服太多。配合Element Plus组件库,哪怕是没写过前端的人,也能快速把表单、表格、对话框这些常用界面搭出来。

MyBatis-Plus的选择,是整个后端开发效率的关键。办公系统的业务大多是单表或多表联合的分页查询,如果用原生MyBatis,每个实体都要手写一套XML的CRUD,工作量直接翻倍。MyBatis-Plus提供通用Mapper和通用Service,单表增删改查零SQL,分页插件一加就完事。它的条件构造器在处理复杂查询条件时尤其好用,比如考勤统计时根据部门、日期范围、关键字筛选,一句LambdaQueryWrapper就解决了,不需要写一堆动态SQL拼接。

MySQL 8.0在这个组合里属于“默认正确”的选择。相比5.7,8.0在性能、窗口函数、CTE、默认字符集utf8mb4上都领先一大截。尤其是utf8mb4,如果你系统里可能出现生僻字或者表情符号,5.7的utf8其实有点力不从心,8.0默认就给你妥善处理了。

1.3 “含文档”这个亮点含金量有多高

我必须单独说一下“含文档”这件事。做Java Web项目,源码其实是最不稀缺的东西,GitHub上一搜一大把。真正稀缺的,是能把项目跑起来的配套说明。很多开源项目什么代码都给了,唯独不给数据库初始化脚本,或者给了脚本却不说明用哪个版本MySQL执行,导致新手在各个报错之间反复挣扎。

这个项目带文档,意味着至少包含三样东西:数据库初始化SQL、接口说明、部署运行指南。数据库初始化SQL是最关键的,因为办公管理系统的表结构非常依赖设计,部门表、用户表、审批表之间的外键关系,通过SQL一眼就能看懂。接口说明在前后端分离的项目里也至关重要,它告诉你登录接口返回什么字段、提交请假单需要传哪些参数、分页查询的page和limit怎么命名。有了这些,后端的Controller层和前端的API封装才能对得上。

具备完整文档的项目拿来做学习是特别顺畅的。你能直接从“已知能跑通的代码”往回倒推结构,而不是在猜测中消耗热情。

2. 部分核心模块的设计与实现路径

2.1 用户权限模型:RBAC的落地姿势

办公系统里权限这块,老实说方案可以复杂到上RBAC + 数据权限 + 操作审计,但实际项目中90%都用的是最经典的RBAC模型:用户表、角色表、菜单表,外加用户角色关联表和角色菜单关联表。这个项目的用户、角色、菜单三张主表外加两张关联表,属于标准配置。

我重点说一下菜单权限怎么控制。后端在用户登录成功后,一次性查询出该用户所有可见的菜单,以树形结构返回给前端。前端拿到菜单树后,动态生成侧边栏和路由。这里有个实用细节:菜单表里通常包含“父菜单ID”和“路由地址”两个字段。父菜单ID用来拼树,路由地址对应前端Vue Router里配置的path。Vue 3中动态添加路由要用router.addRoute,而且要注意在用户退出登录后重置路由,不然下一个用户登录时会看到上一个用户的菜单残留。

按钮级别的权限,这个项目不一定做得很细,但如果要做,思路通常是在菜单表里加一个“按钮权限标识符”字段,后端在查询用户菜单时一并返回,前端用自定义指令控制按钮显隐。这类实现的好处是权限设计能覆盖到页面里的“新增”“删除”“导出”这些具体动作,而不是只能控制到整个菜单。

另外要提醒一句:前端控制按钮显示只是体验层面的优化,真正的安全校验必须在后端做。后端接口上加上权限注解,比如@PreAuthorize,用SpEL表达式判断当前用户是否拥有指定权限标识,这样即使有人跳过前端直接调API,也会被拦截下来。

2.2 数据库表设计的几个关键细节

办公管理系统的数据库表通常在20到30张之间。比如这个项目,会包含sys_user、sys_role、sys_menu、sys_dept、oa_notice、oa_leave、oa_attendance、oa_meeting这类表。这些表的设计有几个细节特别值得注意,直接决定你后续开发的顺滑程度。

逻辑删除标志是必须的。办公系统里的数据都是真实业务数据,比如员工离职、公告撤销、请假单作废,你不能真的把行删掉,否则后续审计和统计都成问题。所以每张业务表都要有一个deleted字段,MyBatis-Plus的@TableLogic注解配上这个字段,能实现“查询自动过滤已删除数据、删除操作自动转成更新”的效果。比如员工离职改成deleted=1,但考勤记录里依然能查到这个人当月的打卡数据。

创建时间和更新时间这种审计字段,建议每个表都要有。别小看这两个字段,排查“2107这个单子是谁在什么时候建的”这类问题,全靠它们兜底。MyBatis-Plus的字段自动填充功能可以做这件事:实体类上标注@TableField(fill = FieldFill.INSERT)和@TableField(fill = FieldFill.INSERT_UPDATE),然后定义一个MetaObjectHandler的实现类,在insert和update时自动填入当前时间,省掉在每个Service里手动set时间的繁琐。

审批状态字段的定义要提前统一。我见过不少项目,等待审批用0,同意用1,驳回用2,但到了会议审批模块,状态含义就变了,搞得前端写死一堆映射关系,后期维护极其痛苦。正确的做法是定义一个统一的状态枚举,比如用Integerstatus表示未提交、待审批、已通过、已驳回、已作废,每个模块的表都复用这套约定,前端再统一封装一个状态标签映射组件,就不会出现各模块状态颜色对不上的尴尬。

2.3 前后端交互约定的建立

目前项目多为前后端分离开发。把交互约定先想清楚,能省掉大量联调时间。

接口返回结构统一是最基础的一条。这个项目的后端会定义一个统一响应体,通常长这样:{code: 200, message: "操作成功", data: {}}。所有Controller的返回值都放进这个壳子里。前端配合Axios拦截器,在响应拦截器里判断code,非200统一弹错误提示,200则直接把data解出来用。这样后端只需专注业务逻辑和返回数据,前端只需规范统一处理异常和成功分支。

状态码设计上,我建议区分两类:HTTP状态码用于表示请求本身是否成功,业务状态码用于表示业务逻辑是否成功。登录过期返回401,权限不足返回403,参数校验失败返回400,这属于HTTP层语义;至于“请假单已经在审批中,不能重复提交”这类就属于业务状态码。在一套系统里,后端把这两类区分清楚,前端拦截器处理起来才不别扭。

Date类型序列化的坑也要提前踩掉。后端LocalDateTime默认序列化成2025-04-15T10:30:00这种带T的ISO格式,前端拿到手显示就会别扭。常见做法有二:一是在Jackson配置中统一日期格式,二是在实体字段上用@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")。对整个系统来说,我更倾向全局配置,不然每个字段都要加注解,很容易漏。

3. 实操过程与核心环节实现

3.1 后端项目骨架搭建的关键步骤

先把后端骨架搭起来。你拿到的源码结构一般会是这样:一个Maven多模块或者单模块的Spring Boot工程,包含controller、service、mapper、entity、dto、config、common这些包。

第一步是改配置。application.yml里最重要的几个配置项包括数据源、Redis(如果有)、文件上传路径和日志级别。对于这套项目,数据源配置必须注意MySQL 8.0的驱动类名。以前的com.mysql.jdbc.Driver已经不用了,现在必须写com.mysql.cj.jdbc.Driver,同时JDBC URL上要加上时区参数:

spring: datasource: url: jdbc:mysql://localhost:3306/oa_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456

把数据源配置好后,启动类上加上@MapperScan注解扫描Mapper接口所在的包。MyBatis-Plus的插件配置也要在这个阶段配好,主要是分页插件和乐观锁插件。分页插件直接关系到查询接口能否正确处理page和size参数,不配的话,写着Page<User>的查询方法会退化成查全表再内存分页,数据一多就卡成PPT。

登录认证这块,项目一般会用JWT。我建议你把JWT的密钥和过期时间放到配置文件里管理,不要写在代码里。前端登录成功后拿到token,后续每次请求通过Authorization请求头带上。后端用一个拦截器或过滤器去校验token,校验通过后从token里解析出用户ID和用户名,放在ThreadLocal或RequestScope里,后续Service层需要知道“当前操作人是谁”时直接取,省得每次从Controller手动传参。

登录流程里还有个细节:密码加密。不要用MD5,一定要用带盐的哈希算法。BCrypt是主流选择。Spring Security自带BCryptPasswordEncoder,项目如果是自己写拦截器实现的认证,也可以单独引入Spring Security Crypto组件只用它的BCrypt加密工具。注册或初始化用户时,存进数据库的必须是BCrypt加密后的密文,而不是明文。这个问题在课设项目里出现频率特别高,一定不要图省事存明文。

3.2 前端Vue3部分的组织方式

前端项目若用Vite构建,那整体工程结构比Webpack时代的“义乌小商品市场”清爽得多。src目录下,通常是api、views、components、router、store、utils这几个子目录。

Axios封装是前端里面值得优先看的部分。一套好的Axios封装会在请求拦截器里自动把token加到请求头上,在响应拦截器里统一处理业务错误码、HTTP错误码、登录状态过期。办公系统里会频繁出现“提交表单时后台返回‘该员工本月已提交过加班申请’”这样的业务错误,拦截器里判断业务码统一弹Message提示,前端每个页面的错误处理代码可以精简到只关心成功路径。

路由和菜单的联动是Vue3项目里最考验封装功底的地方。这个项目的路由配置文件里,静态路由通常只写登录页、404页这类公共页面,业务页面全部走动态路由。登录成功拿到菜单数据后,前端把这些菜单数据映射成RouteObject结构,用router.addRoute动态挂载。菜单配置里带有图标名称时,渲染侧边栏时需要异步加载Element Plus图标组件,这个细节做不好,菜单里的图标就会全部显示成空白方块。

组件拆分层面,办公系统的数据表格使用频率极高。强烈建议把“搜索表单 + 表格 + 分页”的套路抽成一个通用组件或组合式函数。这样用户管理页、公告管理页、考勤记录页,都复用它,只传入对应的列配置和查询API,页面代码能减少一半。

3.3 审批流的通用实现套路

请假审批这个具体模块,代码逻辑很值得手把手看一遍。它的数据库表通常长这样:oa_leave表存请假单基础信息,字段包含申请人ID、请假类型、开始时间、结束时间、请假天数、原因、当前状态;oa_leave_record表存审批记录,每个审批人审批一次插入一行,包含审批人ID、审批意见、审批时间、审批结果。

提交请假单时,后端要做两件事。第一,校验时间是否冲突,比如同一个员工在同一时间段是否已有审批通过的请假单,或者是否重叠了出差申请。第二,把状态置为“待审批”,并且向直属主管的待办列表里插入一条待办记录。这里的“直属主管”怎么确定,最简单的方式是在部门表上有一个leader_user_id字段,把你的用户表与该字段关联,要找到部门Leader时直接查询用户信息。如果组织架构更复杂,比如有副主管、有代理审批人,那就得在部门表上扩展更多字段或者引入更完整的审核流程配置。

审批人操作时,点击“通过”,后端做的是更新请假单状态为“已通过”,同时在审批记录表插入一条结果。点击“驳回”同理,只不过状态变成“已驳回”,并且可以附带驳回原因,返回到前端时在原表单下方显示红字原因。

这套简单审批流最大的优点是容易看懂、容易改。你要让部门主管审批完跳过同级、直接到人事复核,只需多加一个状态节点,再加一条更新状态的判断逻辑,改动成本很低。等业务复杂到需要多级审批、条件审批、并行审批时,再考虑集成Flowable或Activiti就行,没必要一开始就上重型框架。

3.4 前后端联调时最容易卡住的几个点

先把运行环境看好。后端默认端口通常8080,前端开发服务器默认5173,两者不在一个端口上。生产环境可以用Nginx把前后端放同一个域名下,通过/api前缀把请求反向代理到后端,绕开跨域;开发环境则直接在后端配置CORS,放行http://localhost:5173这个来源。

后端配置CORS时注意要允许携带凭证。办公系统登录后如果采用Cookie + Session方式,那allowedHeaders里必须明确放行Authorization、Content-Type等常用请求头。如果使用JWT,则请求多为无状态的Authorization头,但仍然需要允许前端跨域读取响应头中的token信息。这里的细节在于,允许凭证和允许所有来源不能同时为真,allowedOrigins要精确到具体的开发地址,否则浏览器会直接拦截。

另外,接口联调时最常遇到的是参数名对不上。后端分页接口若是用current和size,前端就得传current和size;后端的pageNum/pageSize,前端就得跟着改。这类问题靠的是文档,完全无法凭脑补解决。

3.5 部署到服务器上的标准路径

部署这块很多新手容易被卡住。核心思路是:后端打包成Jar包运行,前端打包成静态文件用Nginx托起来。

后端Way:

mvn clean package -DskipTests java -jar target/oa-system-1.0.0.jar

如果服务器内存有限,可以用-Xms256m -Xmx512m限制JVM堆内存,办公系统这种规模的项目勉强够跑。前提是服务器上加个Swap空间或者干脆把并发量压得低一点。

前端构建:

npm install npm run build

构建完后dist目录里的静态文件,拷贝到Nginx的html目录下,或者配置一个server块指向它。Nginx配置里最关键的一段则是反向代理,把所有以/api开头的请求转发到后端接口服务上,这样前端和后端都从同一个端口对外提供服务,就不存在跨域问题了。

location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

这里有个细节:proxy_pass http://127.0.0.1:8080/;末尾的斜杠会把/api/logout重写为/logout。如果你的后端Controller里的请求映射本身就带/api前缀,那Nginx配置里斜杠的处理方式就要反过来。这个细节会导致接口404,排查起来却很容易被忽略。

4. 常见问题与排查技巧实录

4.1 版本兼容性问题

开发环境上最容易踩第一脚坑的就是JDK版本。Spring Boot 2官方推荐用JDK 8或JDK 11,JDK 17虽然让很多上Spring Boot 3的项目受益,但拿到这个项目源码时,如果本地是JDK 17,很可能会遇到编译错误,因为依赖的旧库里有些反射调用或第三方字节码在17的模块系统下运行不了。稳妥的做法是,在项目里配置Maven编译插件,把source和target设为1.8,并在本地装JDK 8或JDK 11来跑。

Vue 3前端项目也有版本坑。不同Node版本下,Vite构建有时会报出各种依赖冲突。解决办法是看package.json里的engines字段要求,以及优先使用LTS版本的Node。记住一个原则:项目锁哪个Node大版本,就尽量用哪个,不要无脑升级最新版。

MySQL版本上,8.0和5.7最明显的差异排序规则上。8.0默认字符集utf8mb4,排序规则是这个较为严格的utf8mb4_0900_ai_ci;而5.7常用的utf8mb4_general_ci。如果你把数据库导出后到另一台5.7服务器上恢复,大概率出现字符集不支持的报错。所以在项目初始化脚本里,建库语句直接把字符集写清楚,别依赖MySQL全局默认配置:

CREATE DATABASE oa_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

4.2 跨域与登录态丢失

前后端联调时,“前端能登录,但一刷新登录状态就没了”是高频问题。排查思路分两大类:如果是JWT方案,看前端是否把token持久化到localStorage或sessionStorage,刷新页面后是否从store初始化;如果token存了但还是丢失,看路由守卫里拿token的判断逻辑,留意当前用户信息是不是异步获取的,要等接口返回后再放行页面跳转。

如果是Session方案,重点排查后端CORS配置有没有allowCredentials(true),前端Axios有没有配置withCredentials: true,以及响应头里Set-Cookie的SameSite属性。特别是Chrome浏览器的新版本会对跨域带Cookie的行为限制得更严格,很多旧项目上线后莫名登录失效,都是SameSite属性没有设置成None造成的。

4.3 时间与数字的“灵异问题”

办公系统里最多的数据就是日期时间。考勤打卡、请假起止、审批时间,每一个都涉及时间处理。我已经不止一次见过有人把LocalDateTime传到前端,显示成2025-04-15T10:30:00.000+00:00,然后用户一脸懵地发现时间比真实时间少了8个小时。

这个问题的根源是时区。MySQL连接串里如果不指定serverTimezone,驱动会用JVM默认时区。国内服务器时区一般没问题,但云数据库的服务器如果设在其他地区,时区偏移就会很诡异。正确做法就是在JDBC URL里把serverTimezone=Asia/Shanghai固定下来。

前端拿到时间后也要做格式化处理。要么后端统一返回格式化好的字符串,比如yyyy-MM-dd HH:mm:ss;要么前端写一个全局的日期格式化函数。不要在页面上到处访问时间字段然后各显神通地格式化,同一套系统里时间展示格式出现三四套模样的混乱感,用户第一眼就会觉得很不专业。

4.4 MyBatis-Plus使用中的高频坑

MyBatis-Plus虽然好用,但有些坑不提前躲开,排查起来相当费劲。最典型的是逻辑删除字段和唯一索引冲突。比如用户表里设置了用户名字段为唯一索引,用MyBatis-Plus逻辑删除用户后,该用户名下次再注册时,由于数据库里实际还留着deleted=1的旧记录,会触发唯一索引冲突,导致注册失败。解决办法是索引设计时把deleted字段作为联合唯一索引的一部分,比如(username, deleted),这样逻辑删除后的同用户名就能再次注册。

第二个问题是条件构造器里的eq和like大小写敏感问题。MySQL默认字符串比较不区分大小写,如果你查username = 'Admin',可能把admin也查出来。办公系统里用户名如果是登录凭证,建议数据库里用utf8mb4_bin排序规则,或者在后端代码里统一转大写再比较,避免账号大小写歧义。

第三个问题是自动填充没生效。写成fill = FieldFill.INSERT_UPDATE后,如果MetaObjectHandler这个类忘了加@Component注解,后台创建人、创建时间就永远是空的。这类问题IDE不会自动报错,只有等你查“为什么数据表里没有创建时间”时才会发现。

4.5 前端常见的渲染与打包问题

Vue3的setup函数里,如果你用ref包了一个对象,修改时不小心把整个对象重新赋值为新对象,页面往往会重新渲染,但如果你直接用const state = reactive(...)来维护,就不用担心这个问题。办公系统里那些“保存后列表没刷新”“弹窗关闭后表单还残留上次数据”的问题,大多是因为响应式数据没有正确重置。

打包上传后首页白屏,也是前端新手常见痛点。Vite默认的base路径是/,如果你的前端部署在Nginx子路径下,比如http://server/oa/,静态资源会去请求http://server/assets/xxx.js,自然404。处理方法是在vite.config.ts里设置base: './',让打包后的资源路径变成相对路径。

5. 项目文档与后续扩展方向

5.1 “含文档”项目的正确打开方式

拿到这种带文档的源码,先不要急着import到IDE里就开跑。我的习惯顺序是:先看README,再看数据库初始化SQL,最后才看代码。README会告诉你项目怎么启动、前后端用什么命令跑起来;SQL能让你对表结构有个整体认知,知道用户怎么造出来、有哪些模块;代码则是到这一步才去看具体业务怎么实现的。

跑通之后,建议自己在数据库里随便改点东西、加个字段,追踪这个字段在Controller、Service、Mapper、前端页面里走过的路径。这种“从数据库字段到前端按钮”的全链路追踪,是理解一个项目的最佳方式。调试工具里给登录接口打个断点,看看从请求进入Controller后,一个参数是怎么一步步流转的,比躺着读十遍代码都有用。

5.2 如果你想在这个项目上继续加功能

办公管理系统的扩展空间其实很大。最值得做的是把审批流真正完善起来。当前的简单审批流只支持一条线性路径,你可以引入Flowable或Camunda,把请假、报销、采购申请都纳入同一套流程引擎里去。这意味着要新增流程定义表、流程实例表、任务表,前端的待办中心也要重新设计,让它按任务类型而不是按业务表单类型去渲染。这个方向成熟后,系统的可复用性会高出一个量级。

其次是引入更细的权限划分。部门级数据权限可以考虑切面或注解的方式,在部门经理的账号下限定只能看本部门数据,而不是让他直接看到全公司所有人的考勤。这类“数据权限”是办公管理系统从“演示级”走向“生产级”的重要分水岭。

再就是引入消息推送机制。现在的待办提醒很多停留在页面里的小红点,如果能在企业微信或飞书的应用消息里同步推送“你有一条新的请假审批待处理”,这会让整个系统的使用体验直接上一档。技术实现也不复杂:抽出统一的消息服务接口,对接企业微信或飞书的Webhook接口就行。

如果你打算把它当作毕业设计或学习项目,可以增加更直观的数据可视化:仪表盘上展示本月考勤汇总、各部门请假趋势、审批平均时长。这部分用ECharts或者Vue3生态的图表库都能实现,后端只需要提供聚合查询接口即可。

写在最后的一段实操体会

根据我个人经验,Java Web类办公系统看起来业务简单,但真正做到“办公又好用”其实很考验开发者的全局设计能力。这个项目能把Spring Boot 2 + Vue 3 + MyBatis-Plus + MySQL 8.0这套组合整合得比较完整,已经具备了一个生产级OA系统的雏形。拿到手之后建议从“用户登录→建单→审批→统计”这条主线跑一遍,跑通后再去碰边角功能。

最后再分享一个小技巧:不管你是学习还是二次开发,拿到这种项目第一件事就是备份数据库初始化SQL。然后在本地跑通后立刻执行一次全量导出,生成一份自己的“恢复快照”。后面再怎么改代码、改了表结构、删了测试数据,只要还能从这份快照恢复到初始状态,你就有无限次重来的底气。办公管理系统的开发就是个不断试错的过程,有这个保命手段在,胆子就能大很多。

相关新闻

iOS逆向分析实战:拆解直播间送礼功能完整链路

iOS逆向分析实战:拆解直播间送礼功能完整链路

做iOS开发这些年&#xff0c;经常被问到一个问题&#xff1a;直播间里的送礼物&#xff0c;从用户点下按钮到全屏动画弹出&#xff0c;背后到底经历了多少环节&#xff1f;答案远比想象中复杂。一次完整的“送礼物分析”&#xff0c;基本能覆盖iOS App里最典型的客户端-服务端协…

2026/10/20 16:52:04 阅读更多 →
OpenCV 3.2.0 + VS2015 + opencv_contrib 编译实操指南

OpenCV 3.2.0 + VS2015 + opencv_contrib 编译实操指南

简介&#xff1a;本资源为OpenCV 3.2.0在Visual Studio 2015平台下的完整编译产物包&#xff0c;专为计算机视觉初学者与项目开发者设计&#xff0c;解决Windows环境下OpenCV核心库与contrib扩展模块难以一键集成的痛点。压缩包含456个文件&#xff0c;涵盖280个hpp头文件&…

2026/10/20 16:52:04 阅读更多 →
基于Spring Boot的非遗传承系统:从展示到商城交易闭环设计与实现

基于Spring Boot的非遗传承系统:从展示到商城交易闭环设计与实现

“非遗”这两个字一听就带点遥远和厚重的味道&#xff0c;但真要把它做成一个能让用户逛起来、买得动的产品&#xff0c;拼的还是技术基本功。我最近正好带完一个基于 Java Spring Boot 的非遗传承系统项目&#xff0c;系统里既要做非遗项目展示、传承人管理这些偏内容侧的模块…

2026/10/20 16:51:02 阅读更多 →

最新新闻

Ubuntu下PostgreSQL安装配置与Navicat连接指南

Ubuntu下PostgreSQL安装配置与Navicat连接指南

做后端开发或者数据相关工作的朋友&#xff0c;应该都经历过这么一幕&#xff1a;好不容易在 Ubuntu 上把 PostgreSQL 装好了&#xff0c;却发现命令行操作没问题&#xff0c;客户端工具却怎么都连不上&#xff1b;或者反过来&#xff0c;Navicat 装好了&#xff0c;连接时报一…

2026/10/20 17:33:06 阅读更多 →
uniCloud Serverless 实战:英语学习小程序全链路开发

uniCloud Serverless 实战:英语学习小程序全链路开发

简介&#xff1a;这是一套面向英语学习者与小程序开发者的完整项目源码&#xff0c;基于uniapp与uniCloud Serverless架构构建&#xff0c;可直接在微信环境中运行。项目集生词本记录、拍照识别单词、内置词典查询、AI语音翻译、跟读打分评测于一体&#xff0c;并针对考研人群补…

2026/10/20 17:33:06 阅读更多 →
WiFi指纹室内定位实战:从原理到Demo搭建

WiFi指纹室内定位实战:从原理到Demo搭建

1. 室内定位的现实困境与WiFi方案的破局点实话实说&#xff0c;第一次接手“基于WiFi的室内定位”这个方向时&#xff0c;我心里是打鼓的。日常大家用GPS导航已经习惯了&#xff0c;掏出手机就能知道自己在哪&#xff0c;但一旦走进大型商场、地下车库、机场航站楼&#xff0c;…

2026/10/20 17:33:06 阅读更多 →
大模型后端长对话记忆架构全解析:从无状态到有状态

大模型后端长对话记忆架构全解析:从无状态到有状态

做后端的人都有过这种体验&#xff1a;用户和AI助手聊了三十轮&#xff0c;前二十轮里明明已经把需求背景、偏好、禁忌都交代清楚了&#xff0c;结果聊到第三十一轮&#xff0c;AI突然反问一句“您刚刚提到的那个项目背景&#xff0c;方便再描述一下吗&#xff1f;”当场血压就…

2026/10/20 17:33:06 阅读更多 →
D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

最近不少做视觉入门的朋友卡在第一关——把D435相机的画面真正“看”到屏幕上。很多人买了一台Intel D435深度相机&#xff0c;兴冲冲插上USB线&#xff0c;结果CPU占用飙高、程序报错、画面黑屏&#xff0c;折腾一晚上就劝退了。其实用D435搭配OpenCV显示彩色图和深度图&#…

2026/10/20 17:33:06 阅读更多 →
自考论文降AI率实战:智能体改写与人工复核全流程

自考论文降AI率实战:智能体改写与人工复核全流程

自考论文写到提交前&#xff0c;最让人血压升高的往往不是查重率&#xff0c;而是学校突然要求提交一份AI检测报告。A同学就是这种状况&#xff1a;论文初稿在查重系统里已经降到合格线&#xff0c;结果学院的通知里多了一句“AI疑似率不得超过20%”。他拿自己的稿子去测了一下…

2026/10/20 17:32:04 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →