首页 / 资讯中心 / 文章详情

Spring Boot与Vue前后端分离的工时管理系统源码解析及部署避坑指南

Spring Boot与Vue前后端分离的工时管理系统源码解析及部署避坑指南 ★ FEATURED ARTICLE
简介这是一套基于前后端分离架构的 Java 项目工时管理系统源码oak project主要服务于需要统计项目工时、核算人工成本的中小团队也适合 Java Web 学习者用作企业级管理系统实战参考。系统围绕员工工时上报、原型预览与版本管理、UI 效果图分发三大模块展开支持实时动态展示项目投入后端采用 Spring Boot前端使用 Vue数据库基于 MySQL 8代码中保留了环境配置与数据库脚本便于本地部署和二次开发。压缩包整体结构清晰共 851 个文件约 4.16MB以 Java、Vue、JS、SCSS 源文件为主同时包含 XML、YML、SQL、PNG、SVG 等配置、脚本和静态资源并附有启动/打包脚本与多环境配置可支撑从代码阅读、环境搭建到功能扩展的完整学习路径。资源目前已有 1028 人学习/下载适合希望借鉴前后端分离架构、工时统计流程和原型托管实现细节的开发者。1. 一份能直接跑的 Java 项目工时管理系统源码到底解决了什么如果你在一个 10 人左右的研发团队待过大概率经历过后端、前端、产品、UI 在微信群里传原型包、月底用 Excel 接龙统计工时的日子。这份 Java 项目工时管理系统源码oak project把工时统计、原型预览、效果图管理收进同一个 Web 应用员工上报工时按项目、按人员实时汇总投入原型和 UI 切图传上去就生成链接点开即看还带版本记录。服务端 Spring Boot 前端 Vue跑在 Java 1.8 和 MySQL 8 上前后端分离没有微服务那一堆依赖。适合不想买商业项目管理系统的小团队也适合想读一份完整前后端分离源码的 Java 工程师——哪怕你 Java 基础刚补完按文档把环境配好也能把这套东西跑起来。2. 系统骨架与技术选型Spring Boot Vue 的前后端分离是怎么分工的2.1 前端 Vue 管展示后端 Spring Boot 管数据这条边界为什么合适前后端分离这个词在 Java 圈子里提了好几年但很多小团队的“前后端分离”只是把 HTML 扔进 Tomcat接口还是 MVC 那套。oak project 的做法更彻底Vue 负责页面渲染和交互Spring Boot 只暴露 REST 接口两者通过 JSON 通信。这样做的好处很实际——前端改版不用重新编译后端后端加接口不用碰页面。对团队意味着只要约定好接口结构前端和后端可以并行开发排期能压下来一周左右。从资源本身也能看出这个设计。压缩包里的 main.css、reset.min.css、font-awesome.min.css、ie9.css、noscript.css 都是构建后的前端静态资源说明交付物里已经包含了打包好的前端产物而 .env.development、.env.demonstrate 这种文件是 Vue CLI 在构建和本地开发时读取的环境变量文件。后端 Spring Boot 这边典型的接口长这样RestController RequestMapping(/api/work-hour) public class WorkHourController { PostMapping(/record) public Result submit(RequestBody WorkHourVO vo) { // 校验项目ID、工时时长、上报日期是否合法 // 检查当天是否已提交过避免重复上报 // 写入 work_hour 表并返回自增ID return Result.ok(); } }这段代码的核心在 RequestMapping 和 PostMapping 两个注解前者统一了接口前缀后者把 POST 请求映射到 submit 方法。RequestBody 表示请求体是 JSON前端 axios 传什么结构后端 VO 就接什么结构。Result 是统一返回体里面至少包含 code、message、data 三个字段这样前端拿到响应后只需要判断 code 是否为 0不用每个接口单独处理异常分支。对应前端调用一般会用 axios 封装一个实例// src/api/workHour.js 中的典型写法 import axios from axios const api axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) export function submitWorkHour(data) { return api.post(/work-hour/record, data) }这里有两个参数值得留意。baseURL 不是硬编码的它从 process.env.VUE_APP_BASE_API 读取而 VUE_APP_BASE_API 是在 .env.development 或 .env.demonstrate 里定义的——这就是下一节要说的环境变量机制。timeout 设成 10000 毫秒避免员工在弱网下提交工时请求一直转圈实际上后端已经写库成功了用户又重复提交产生双倍工时。这个坑我在集成别的系统时踩过一次后来凡是写上报类接口前端一定显式设置 timeout。2.2 .env.demonstrate 与 .env.development环境变量把开发、演示、生产隔开Vue CLI 有个约定项目根目录下的 .env.development 在 npm run serve 启动本地开发服务器时自动加载.env.demonstrate 则通过指定 mode 来加载。压缩包里同时出现这两个文件说明交付方把“本地开发”和“演示环境”分开了。.env.development 指向本地后端地址比如 localhost:8080.env.demonstrate 指向部署好的演示服务器地址。这样同一个前端代码包切环境只改文件不改代码。# .env.development 示例按交付资源推断的常规写法 NODE_ENVdevelopment VUE_APP_BASE_APIhttp://localhost:8080/api VUE_APP_PROTOTYPE_URLhttp://localhost:8080/prototype# .env.demonstrate 示例 NODE_ENVdemonstrate VUE_APP_BASE_APIhttp://your-server-ip:8080/api VUE_APP_PROTOTYPE_URLhttp://your-server-ip/prototype之所以用两个文件而不是一套配置原因很直接原型链接和效果图链接在演示模式下必须用服务器地址不然发给客户的原型链接在手机上打开是 localhost谁都访问不了。这一点对原型预览功能尤其关键后面第四章还会展开。再来看压缩包里的 package.bat、run-web.bat、build.bat。三个脚本的分工我按常见做法给你拆一下build.bat 负责构建前端产物内部通常是npm run build或npm run build:demonstrate执行完输出 dist 目录package.bat 负责后端打包典型内容是mvn clean package -DskipTests产出可执行 jarrun-web.bat 负责把前后端串起来——先检查端口占用再启动 Spring Boot jar然后用静态资源方式托管前端 dist。一个 Windows 环境下的启动脚本大致长这样echo off rem 启动后端服务jar 包名按实际产物改 java -jar target/oak-project.jar --spring.profiles.activedemonstrate这里有个关键参数--spring.profiles.active它决定后端读 application-demonstrate.yml 还是 application-development.yml。很多新手直接把 jar 双击跑起来看到控制台报数据库连接失败就是因为默认 profile 里没有配置 MySQL 8 的连接串。后面避坑章节会专门讲。2.3 Java 1.8 MySQL 8为什么说这是小团队最稳的组合选 Java 1.8 不是因为它新而是因为它旧得稳定。绝大多数公司服务器上的 JDK 还是 1.8没有升级到 11 或 17 的动力Spring Boot 2.x 对 1.8 的支持非常成熟网上随便一搜就是各种踩坑记录团队里任何一个 Java 工程师都能上手。MySQL 8 虽然比 5.7 晚但它默认字符集是 utf8mb4存 UI 效果图的文件名、原型备注里的 emoji 都不会乱码而且窗口函数对工时成本按周汇总这类需求很省事。更重要的是这两样在小团队现有的服务器上大概率已经装好了不需要为这个系统单独引入 Redis、MQ 之类的基础设施。当然选型也有代价。Java 1.8 的内存占用比 Go 那类编译型语言高启动一个 Spring Boot jar 通常要吃 300MB 左右内存如果团队服务器只有 1GB 内存就得在配置里调小 JVM 堆否则会出现系统卡死。常见做法是在启动脚本里加-Xms256m -Xmx512m把堆内存压下来前提是业务量不大。这个项目的定位本来就是轻量级管理工具同时在线人数一般不会超过几十人这样的配置完全够用。3. 工时统计模块拆解从员工上报到人工成本核算的完整链路3.1 员工上报的数据流一次 submit 背后发生了什么工时统计是整个系统里逻辑最重的一块。员工打开前端页面选择项目、填写工时数和备注点提交后端收到请求后做三件事校验、去重、写库。校验指的是项目ID存在、工时数大于 0 且不超过 24、日期不是未来日期去重指的是同一员工同一天同一个项目不能提交两次否则月底统计会出现虚增写库则是把记录落到 work_hour 表。一张典型的 work_hour 表按业务需求反推大概是这些字段字段名类型说明idbigint主键自增staff_idbigint员工ID关联员工表project_idbigint项目ID关联项目表work_datedate上报日期hoursdecimal(4,1)工时数支持0.5小时粒度remarkvarchar(255)备注说明create_timedatetime创建时间hours 用 decimal 而不是 int是因为很多团队上报粒度是半小时甚至一刻钟。如果设计成 int员工填 3.5 小时就被迫四舍五入月底统计误差会滚雪球。remark 加 255 的长度限制避免有人把整段代码贴进来撑爆字段。另外 create_time 和 work_date 是两个概念前者是这条记录的落库时间后者是员工实际干活的日子统计时必须以 work_date 为准否则补报昨天的工时会被记到今天。去重逻辑在正常业务里一定要靠唯一索引兜底而不是只靠代码判断。我见过只靠select count(*)判断的版本高并发下两个请求同时通过校验双双写库成功数据直接翻倍。正确的做法是在表上建唯一索引ALTER TABLE work_hour ADD UNIQUE KEY uk_staff_project_date (staff_id, project_id, work_date);建了唯一索引之后即使并发请求同时进来数据库层面也只允许一条成功第二条插入直接报 Duplicate entry 异常。后端把这个异常捕获后返回“今天已上报过该项目的工时”提示即可。这个细节在 Java 面试题里经常被拿出来问也是真实系统里必须补上的防护。很多人写项目只注重功能实现忽略了唯一索引结果演示时看不出问题一上线多人同时提交就翻车。3.2 从工时到成本成本核算的统计口径光有工时记录还不够项目经理真正关心的是钱。这个系统把员工月薪或时薪折算成单价再用“工时 × 单价”算出项目人工成本。成本核算的 SQL 大体是这样一个按项目聚合的查询SELECT p.project_name, SUM(w.hours) AS total_hours, SUM(w.hours * s.hourly_cost) AS total_cost FROM work_hour w JOIN project p ON w.project_id p.id JOIN staff s ON w.staff_id s.id WHERE w.work_date BETWEEN 2025-01-01 AND 2025-01-31 GROUP BY p.project_name ORDER BY total_cost DESC;这条 SQL 做了三张表的关联work_hour 明细、project 项目、staff 员工。staff 表里有一个 hourly_cost 字段存的是该员工的工时单价。把单价放在员工表而非工时表考虑的是成本单价会变但历史工时不该跟着变——不过更严谨的做法是在 work_hour 表里冗余一个 cost_snapshot 字段上报当天就把单价快照进去否则员工涨薪后历史项目的成本会被重新计算影响项目盈亏结论。要是你打算把这套系统用于正式的成本核算建议加上冗余快照。有了 total_hours 和 total_cost页面上的项目看板就能实时显示“这个月投入了多少人天、烧了多少人工成本”。所谓实时、动态、真实的展示项目投入本质就是后端提供一个聚合接口前端定时刷新。聚合接口的关键是不要每次都扫描全表加上(project_id, work_date)的联合索引或者干脆维护一张按日预聚合的 project_daily_stat 表月末查询直接从统计表取数。聚合接口的常见实现用 MyBatis 的注解 SQL 就够不需要单独引入复杂框架。很多 Java 工程师看到“动态展示”就以为要上 WebSocket 实时推送实际上内部管理工具完全没必要前端定时轮询加一个 loading 态效果一样代码量还少。我在自己团队做类似功能时就是前端的 setInterval 每 5 分钟拉一次统计数据后端接口加 3 秒缓存数据库压力几乎可以忽略。至于 MyBatis Plus 根据实体类自动生成建表 SQL 的那套玩法在这个项目里也能用上——用注解建实体后直接在测试环境跑一遍 DDL比自己手写建表语句省事但生产环境还是建议 DBA 审核过再执行。4. 原型预览与效果图管理把第三方托管换成自己的静态资源服务4.1 原型分发一个链接搞定版本管理和历史回看产品原型在开发流程里是高频被访问的东西。传统做法是设计同学把原型包导出成 HTML压缩成 zip丢到微信群后端同学下载、解压、本地打开看到的往往还是过期版本。oak project 的做法是把原型文件传上服务器通过链接直接访问。链接的形式一般是http://server-ip/prototype/项目名/版本号/index.html。这个功能在实现上依赖两件事文件存储的目录结构和静态资源映射。目录结构一般按三级来组织项目名做一级目录版本号做二级目录原型文件放版本目录里prototype/ |- order-center/ | |- v1.0/index.html | |- v1.1/index.html | └─ v2.0/index.html └─ user-center/ └─ v1.0/index.html后端把 prototype 目录映射为静态资源路径Spring Boot 里一行配置就能搞定Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/prototype/**) .addResourceLocations(file: prototypeDir /); }这里的prototypeDir是配置文件里指定的磁盘路径Windows 上形如D:/data/oak/prototype/Linux 上形如/opt/oak/prototype/。把原型目录放在工程目录之外是为了避免重新部署后端时把原型文件一起覆盖掉。版本管理则是上传时带一个版本号参数系统把文件放进对应版本目录并在版本表里记录创建人和创建时间。前端原型列表页按项目分组展示所有版本点某个版本就是访问对应的 index.html天然实现了历史回看。我见过好几个团队用文件名后缀来区分版本比如原型最终版(2).zip、原型改完不再动.zip时间一长根本分不清哪个是最新的用版本目录管理之后这个问题彻底消失。4.2 效果图管理命名约定和权限边界效果图管理和原型预览共享同一套静态资源映射区别在于效果图一般需要增加一层权限控制。原型是给团队内部看的效果图则可能涉及未发布产品的 UI 截图不该让所有人都能直接拼链接访问。常规做法是给效果图目录加一个简单的 token 校验——访问 URL 时附带一个短 token后端拦截器校验通过才放行而不是把整个系统接上完整的 RBAC 权限体系。命名上我的习惯是强制按“项目_模块_版本_日期”的规则上传比如order_list_v1.0_20250601.png。这个命名规范能让效果图的列表页按文件名自动排序同一个模块的历史版本一眼就能看出新旧省去一张张打开对比的时间。压缩包里同时出现 main.css、font-awesome.min.css 这些前端资源也说明列表页完全可以支持图片缩略图网格展示——CSS 框架已经就位前端只需要一个缩略图组件。font-awesome.min.css 里的字体图标可以直接用来做文件类型标识比如 png 文件显示图片图标zip 文件显示压缩包图标几行代码的事。4.3 为什么说替代第三方原型托管是可行的第三方原型托管工具本身不差但小团队用起来总会遇到几个现实问题免费版原型数量有限制一个项目做完就得清理旧版本原型涉及未公开产品传第三方平台总有数据安全的顾虑团队成员离职后账号交接麻烦。oak project 的这套方案把原型文件放在自己的服务器上访问链接受内网或端口控制版本不清理理论上没有数量限制。代价是要有人维护磁盘空间和静态资源服务的可用性。对一个 10 人团队来说这个代价完全可接受——一块 500GB 的硬盘能放好几年的原型文件而省下来的是第三方工具的会员费和管理成本。5. 部署与运行避坑五个最常见的问题和排查路径5.1 双击 run-web.bat 闪退现象双击 run-web.bat 后黑窗口一闪而过什么都看不到后端也没起来。原因脚本执行异常直接退出。最常见的情况是 JDK 没装或者 JAVA_HOME 没配置Java 命令找不到其次是压缩包解压路径有中文或空格导致 jar 包路径解析失败再往下才是 jar 包本身没打出来。解决先在命令行窗口手动执行java -version看有没有输出。如果提示“不是内部或外部命令”先配 JAVA_HOME 和 PATH。确认 JDK 没问题后不要双击脚本改成在 cmd 里执行call run-web.bat这样能看到完整的错误输出。拿这个项目来说JDK 必须是 1.8装了 17 大概率会在 Spring Boot 启动早期就报 Unsupported class file version 之类的错误。Java 环境配置这块是 java 工程师的基本功但经常被忽略建议团队统一发一份 JDK 安装规范别让每个人自己下。5.2 MySQL 8 连不上时区、驱动、密码加密方式现象后端启动日志里报Access denied for user或者Communications link failure数据库连接池初始化失败。原因MySQL 8 默认认证插件是 caching_sha2_password而某些旧版 JDBC 驱动只支持 mysql_native_password两边握手失败。另外 MySQL 8 的 JDBC 连接串必须显式指定时区否则驱动不知道用哪个时区连接会被拒。解决连接串里加上serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue并且确认 pom 里 mysql-connector-java 的版本在 8.0 以上。如果数据库里已有的用户是用 mysql_native_password 创建的也可以执行下面的 SQL 转换ALTER USER oaklocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;这里有个容易混淆的点allowPublicKeyRetrievaltrue是 MySQL 8 特有的参数不加的话驱动会拒绝获取服务端公钥导致认证失败。很多老项目从 5.7 迁到 8 之后连接报错查来查去最后都是这个参数的问题。5.3 .env.development 改了没生效现象改了 .env.development 里的 VUE_APP_BASE_API重新刷新前端页面接口请求的还是旧地址。原因Vue CLI 的环境变量不是在浏览器运行时读取的而是在构建时注入的。也就是说修改 .env 文件后必须重新执行构建或重新启动 dev server浏览器端拿到的还是打包时写死的地址。这个坑十个人里有八个会踩。解决改完 .env 后先停掉 dev server再重新npm run serve如果用的是打包后的静态资源则要重新执行 build 脚本。判断是否生效可以看前端接口请求的 URL用浏览器开发者工具的 Network 面板直接观察。另外注意 .env 文件不要提交进 Git开发环境和演示环境的地址不一样提交了容易互相覆盖。5.4 原型页面打开但 CSS 和图片全挂现象原型链接能打开 HTML但页面上的图片、样式完全没有只有文字。原因原型文件里引用资源用的是绝对路径比如/assets/css/main.css部署后根路径变化导致资源 404。也可能是静态资源映射只打开了 HTML 后缀没匹配到 css、js、png 文件或者 Nginx 配置里没有正确 alias 到原型目录。解决把原型访问前缀和实际路径对齐。如果用的是 Nginx 托管配置大致是location /prototype/ { alias /data/oak/prototype/; index index.html; }另一个经验是原型包导出时尽量选相对路径不要选绝对路径这样放到任何一层目录下都能正常访问。用相对路径的原型在本地双击打开和放到服务器上访问效果是一致的省去一堆排查时间。5.5 端口被占用导致后端起不来现象启动 jar 时日志报Port 8080 was already in use服务一直起不来。原因服务器上已有其他服务占用了 8080常见的是另一个 Java 服务或者 Tomcat。解决改端口。在 application.yml 里加server.port8081同时把前端 .env 里的 VUE_APP_BASE_API 和 Nginx 代理端口一起改。改完记得前后端两边保持一致的端口否则会出现前端页面正常、接口全部 404 的情况。更规范的做法是用java -jar oak.jar --server.port8081这种命令行参数方式覆盖不用改配置文件部署脚本里写死端口避免多人开发时互相踩。6. 把 oak project 改造成适合自己团队的三个增强点原型版本管理现在已经能用了但如果你想让它在团队里真正落地我建议加一个“版本与迭代里程碑关联”的小功能。具体做法是在版本表里加一个 milestone 字段值填 v1.0、v1.1、v2.0 这样的迭代号原型列表页按里程碑分组展示。这样产品经理打开页面第一眼就能看到当前迭代对应的最新原型是哪个不用自己在链接后面猜版本号。实现成本很低一个字段加一个分组逻辑前后端改动不超过两小时。工时模块的第二个增强点是周报导出。月底手动导 Excel 太被动可以写一条按周聚合的 SQL每周一早上自动把上周的工时明细和成本汇总发到项目经理邮箱。注意导出时要包含员工上报日期和备注否则项目经理看到一笔异常工时还得再进系统查明细。SQL 的写法就是 3.2 节那条聚合语句把时间范围换成自然周再联一张员工表把名字带出来。第三个增强点是成本单价按角色配置而不是纯粹按人。比如初级、中级、高级工程师三个角色各设定一个单价员工表里存角色 ID成本核算时 JOIN 角色单价表。这样员工晋升后只改角色历史成本快照不受影响账目更清晰。综合来看这套系统的核心价值不在功能多花哨而在于数据闭环上报即沉淀、沉淀即汇总、汇总即呈现。从那以后我在团队里推行任何管理类工具都强制走一遍“录入-汇总-导出”的闭环验证确认数据流转没有断点再上线这个习惯帮我避掉了好几次返工。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站