☰
SpringBoot+Vue工作量统计系统设计与实现:从CRUD到闭环管理
2026/10/13 5:35:59 网站建设 项目流程

1. 先别急着写代码:这个工作量统计系统到底要解决什么问题

我做过不少Java全栈项目,但真正让我觉得“值得拿出来讲”的,工作量统计系统算一个。原因很简单:它看起来像是个普通的CRUD,但真正落地之后你会发现,牵涉到的业务逻辑、统计口径、前后端交互、权限控制,远比想象中复杂。尤其当你面对的是一个真实团队、真实项目、真实工时数据的时候,一个数字算错,整个统计报表就废了。

这套系统是基于SpringBoot + Vue + MySQL + MyBatis做的完整实现,源码全部可用。它解决的核心问题是:团队成员每天、每周、每月的工作量到底怎么量化统计?项目投入的工时分布是否合理?哪些任务严重超时?哪些人长期满负荷?这些数据如果靠手工填Excel,收集和汇总就是一场灾难;如果只做个简单的增删改查页面,又很难满足实际管理需求。所以系统的设计目标很明确:把工作量填报、审核、统计、可视化、导出这一整条链路打通,让数据从录入到出报表形成闭环。

适合谁来参考?如果你是刚学完SpringBoot和Vue、想找一个完整项目练手的人,这套系统的代码结构和技术栈非常典型;如果你是在公司需要做一套类似的内部管理工具,这套系统的数据模型和统计思路可以直接借鉴。文章后面会从需求分析、数据库设计、后端接口、前端页面、部署上线、踩坑实录六个部分逐一展开,尽量把设计决策背后的原因也讲清楚。

2. 需求拆解与功能边界:不要一上来就建表

很多新手拿到这类项目,第一反应是打开Navicat建表。这其实是最大的坑。工作量统计系统的核心不是“表怎么建”,而是“工作量怎么定义”。这个定义搞不清楚,后面所有统计逻辑都是空中楼阁。

2.1 工作量的定义方式:填时长还是填任务

我见过两种主流方式:一种是员工每天填报“我今天干了8小时”,系统按分钟累计;另一种是员工填报“我今天完成了哪些任务”,每个任务预估工时,系统按任务完成度折算。第一种实现简单,但数据质量很差——人们填时长的时候普遍会“填满”,导致报表里全是8小时,看不出真实产出。第二种更接近项目管理软件(比如Jira、禅道)的逻辑,但实现起来需要维护任务池、分配机制、审核机制,复杂度上了一个台阶。

我最终选择的是混合模式:员工每天按“任务 + 投入时长”的方式填报工作量,一条记录绑定一个任务;任务可以预先把预估工时和负责人定义好,也可以临时新建。系统的统计维度分三层:按人统计(每个人投入了多少时间)、按项目统计(每个项目消耗了多少时间)、按任务统计(每个任务实际花了多少时间,和预估比是超支还是结余)。这个设计的好处是,填报的粒度可粗可细,但统计的口径是统一的。

2.2 角色权限:谁能填、谁能看、谁能改

工作量数据天然带有敏感性,权限模型必须明确。这套系统设计了三种角色:普通员工、项目经理、管理员。

  • 普通员工:只能填报自己的工作量,查看自己的日报和周报。
  • 项目经理:可以查看自己负责项目下的所有工作量数据,审核成员的填报记录。
  • 管理员:全局数据可见,可以管理用户、项目、任务字典,且可以修正异常数据。

权限控制是在后端做的,用了Spring Boot的拦截器 + 自定义注解,前端根据登录用户的角色渲染不同菜单和按钮。有人会问:为什么不用Spring Security?因为这个系统没有复杂的OAuth2需求,简单的Token鉴权就够了,引入重型框架反而增大理解成本。项目里的拦截器逻辑非常清晰,适合初学者读懂整个鉴权链路的实现。

2.3 统计口径:自然周、自然月,还是按项目周期

工作量统计最容易扯皮的就是时间范围。项目里我做了三套统计维度:

  1. 按日:看某个人某天填了什么,时长多少,有没有异常(比如一天填了超过12小时)。
  2. 按周:看本周每人累计工时,对比本周预期工时(按工作日×8小时计算),判断负荷情况。
  3. 按月:看月度总工时分布,按项目和人员两个维度交叉统计。

这里涉及到一个关键设计决策:统计范围是自然周/自然月,而不是项目启动之后的第N周。虽然自然周会和项目迭代周期错位,导致数据不够“项目化”,但绝大多数管理层习惯看自然周报表,因为要和考勤、工资核算周期对齐。系统的灵活性在于,我预留了按项目时间段筛选的选项,可以自由选择任意起止日期来生成统计报表。

3. 技术选型与关键决策:为什么是SpringBoot + Vue而不是其他组合

标题已经点明了技术栈:SpringBoot + Vue + MySQL + MyBatis。这几乎是Java全栈项目最主流的组合了,但每选一个组件,背后都有对应的取舍逻辑。

3.1 后端:SpringBoot的版本选择与MyBatis的定位

SpringBoot我用的是2.7.x版本,没有上3.x。为什么?因为3.x基于Jakarta EE规范,很多老的开源组件和插件需要额外适配,而2.7作为2.x的最后一个稳定分支,兼容性好、资料多、踩坑成本低。如果你的机器上装的是JDK 17,那用3.x问题不大;如果是JDK 8,那老老实实2.7就对了。

持久层用了MyBatis而不是MyBatis-Plus。原因是我比较在意SQL的可控性,工作量统计涉及大量分组聚合查询,MyBatis的XML配置可以精确控制每一行SQL,配合<where>、<if>这些动态标签,比MyBatis-Plus的Wrapper链式调用更直观。当然,MyBatis-Plus在做单表CRUD时效率很高,但在这个项目里,复杂报表SQL占了很大比重,MyBatis反而更顺手。

数据库连接池用的Druid,监控页面在开发阶段非常好用,能直接看到每一条SQL的执行时间。如果你是第一次跑这个项目,可以在Druid的后台页面观察慢SQL,对优化报表查询很有帮助。

3.2 前端:Vue 2还是Vue 3

这个项目用的是Vue 2 + Element UI。可能有人会觉得都2024年了怎么还不上Vue 3 + Element Plus,坦白说,Vue 3 + Vite + Element Plus的组合现在确实更主流,但选择Vue 2的理由也很现实:项目里用到的一些第三方控件和表格插件在Vue 2生态下最稳定,而且公司存量项目不少还是Vue 2维护,学一遍这套结构可以直接上手工作。如果你是自学,我更建议先把Vue 2的基础弄通,再看Vue 3的差异,因为两者的核心概念(组件、路由、状态管理)是相通的,迁移成本并不高。

前端工程化部分用了Vue CLI,不是Vite。Vite虽然快,但Vue CLI的webpack配置对新手更友好,很多问题在网上都有现成答案。这里提醒一下,Vue环境配置有个高频坑:Node版本和依赖版本不匹配,建议Node 14到16之间比较稳,装Element UI和node-sass的时候能省心很多。

3.3 认证方案:JWT还是Session

登录认证这块我用的JWT。流程是:登录成功后后端生成Token返回给前端,前端存到localStorage,axios请求拦截器统一在Header里带Authorization: Bearer <token>,后端通过拦截器解析Token获取用户信息。这个方案的优点是无状态、横向扩展容易,缺点是Token泄露后无法主动失效,所以项目里给Token设置了过期时间(24小时),并在前端路由守卫里做了登录态检查。

如果你对JWT不熟,可以把它理解成一张“服务器签过名的通行证”。服务器不记录这张通行证的状态,只通过签名验证真伪。所以一旦发出去,在到期之前都是有效的。这里有个细节:用户修改密码后,理论上应该让旧Token失效,但JWT天然做不到这一点。项目里我做了个折中——用户信息表里存一个token_version字段,JWT里带上这个版本号,密码修改后版本号加一,旧Token自然校验失败。这算是个比较轻量的“踢人下线”方案。

4. 数据库设计:工作量统计的命脉在维度建模

这部分是整套系统的地基。表建得合理,后面写SQL就是顺手的事;表建得不合理,每个统计页面都得堆一堆临时逻辑。

4.1 核心表结构

系统里一共有5张核心业务表:用户表(sys_user)、项目表(project)、任务表(task)、工作量填报记录表(work_log)、项目成员关联表(project_member)。另外有角色表、菜单权限表这些相对独立。工作量填报记录表是最核心的,我列出它的字段设计:

字段名类型说明
idbigint主键
user_idbigint填报人
task_idbigint关联任务
project_idbigint关联项目
log_datedate工作日期
work_hoursdecimal(4,1)投入工时,精确到0.5
work_contentvarchar(500)工作内容描述
statustinyint0待审核 1已通过 2已驳回
create_timedatetime创建时间

work_hours用decimal(4,1)而不是float,是因为浮点数在累加统计时会出现精度误差,用定点数最稳妥。status字段为审核流预留了状态位,项目经理处理后数据才进入统计视图。这里建议加一条约束:同一user_id + log_date + task_id 只允许一条记录,避免同一人同一天重复填报同一个任务。如果业务上允许一个人一天在多个任务上切换,那就把task_id换成多对多的方式,但当前版本的设定是“一天一条,一天一件主要任务”,统计口径更清晰。

4.2 统计报表的SQL设计

工作量统计系统里最值得研究的是报表SQL。比如“按月统计每人总工时”:

SELECT u.real_name, DATE_FORMAT(w.log_date, '%Y-%m') AS month, SUM(w.work_hours) AS total_hours FROM work_log w LEFT JOIN sys_user u ON w.user_id = u.id WHERE w.status = 1 AND w.log_date BETWEEN #{startDate} AND #{endDate} GROUP BY u.real_name, DATE_FORMAT(w.log_date, '%Y-%m') ORDER BY month, total_hours DESC

这段SQL的关键在于WHERE w.status = 1——只统计审核通过的数据,防止无效记录污染报表。项目里还用了MyBatis的动态SQL处理多条件筛选,比如按项目、按时间范围、按人员姓名模糊查询:

<select id="selectWorkLogStats" resultType="com.example.vo.WorkLogStatsVO"> SELECT p.project_name, u.real_name, SUM(w.work_hours) AS total_hours FROM work_log w LEFT JOIN project p ON w.project_id = p.id LEFT JOIN sys_user u ON w.user_id = u.id <where> <if test="projectId != null and projectId != ''"> AND w.project_id = #{projectId} </if> <if test="userId != null and userId != ''"> AND w.user_id = #{userId} </if> <if test="startDate != null and startDate != ''"> AND w.log_date &gt;= #{startDate} </if> <if test="endDate != null and endDate != ''"> AND w.log_date &lt;= #{endDate} </if> AND w.status = 1 </where> GROUP BY p.project_name, u.real_name </select>

4.3 一个容易被忽略的索引优化

数据量小的时候,怎么查都很快;数据量一旦涨到几十万条,报表SQL就会变慢。我在work_log表上建了联合索引idx_user_date (user_id, log_date),因为绝大多数统计查询都是先定位“谁”再定位“时间范围”。如果项目维度查询更多,可以再加idx_project_date (project_id, log_date)。这里的关键是索引顺序:等值条件放前面,范围条件放后面,才能命中索引。很多人一上来就给所有字段单独建索引,结果MySQL优化器反而不走索引,这是新手很容易忽略的点。

5. 后端核心实现:接口设计、业务逻辑与审核状态机

后端部分,我从Controller、Service、Mapper三层把核心逻辑拆开讲,重点讲工作量填报的校验逻辑和审核状态流转。

5.1 整体分层与接口清单

项目采用标准的Controller-Service-Mapper分层。Controller只负责接收参数和返回结果,Service层写业务规则,Mapper层写SQL。返回结果统一封装成Result<T>对象,结构是{code, msg, data}。code为200时表示成功,401表示未登录或Token失效,500表示服务端异常。前端axios的响应拦截器统一处理这些状态码,403时自动跳转登录页。

接口清单大致如下:

  • POST /api/auth/login登录,返回Token和用户信息
  • POST /api/workLog/add填报工作量
  • GET /api/workLog/myLogs查询我的填报记录(分页)
  • PUT /api/workLog/audit审核工作量(项目经理/管理员)
  • GET /api/stats/personMonth按月统计个人工时
  • GET /api/stats/projectOverview项目投入分布
  • GET /api/task/listByProject按项目查任务列表
  • POST /api/task/add新增任务
  • GET /api/user/list用户列表(管理员)

每个接口的参数校验都用了JSR 303注解,比如@NotNull、@DecimalMin(value = "0.5"),减少Service层的脏数据判断。

5.2 填报接口的校验链条

/api/workLog/add这个接口看似简单,但涉及好几层校验:

  1. 用户是否登录:由拦截器保证。
  2. 任务是否存在、任务是否属于当前用户所在项目:查task表,比对project_member表。
  3. 工作日期是否可以填未来日期:结合实际业务,当天只能填报当天及之前的数据,不允许预填未来工时,避免月底突击填报造成数据失真。
  4. 是否重复填报:查work_log表,如果同一天同一任务已有记录,则更新而不是新增。

代码里有一段关键逻辑:

WorkLog existing = workLogMapper.selectByUserAndDateAndTask( userId, workLog.getLogDate(), workLog.getTaskId()); if (existing != null) { existing.setWorkHours(workLog.getWorkHours()); existing.setWorkContent(workLog.getWorkContent()); workLogMapper.updateById(existing); return Result.success("已更新当日填报记录"); }

用户通常不会记得自己“填过没有”,与其报错,不如静默转换成更新,体验会好很多。这个细节是我在实际使用中摸索出来的。

5.3 审核状态机:待审核、通过、驳回

审核接口的逻辑是一个简单的状态机:只有状态为0(待审核)的记录允许被流转到1(已通过)或2(已驳回);已通过的记录如果要修正,必须走“驳回重填”的流程。后端实现时加了一层防护:

int rows = workLogMapper.auditWithStatusCheck( id, targetStatus, ReviewStatus.PENDING.getCode(), operatorId); if (rows == 0) { return Result.error("记录状态已变化,刷新后重试"); }

通过UPDATE ... WHERE status = 0这种乐观锁方式,保证并发操作下不会出现状态错乱。初学者容易忽略这种情况:两个审核人同时打开同一条记录,一个点了通过,一个点了驳回,如果都用“无条件更新”,后操作的会覆盖先操作的。加上状态条件,更新行数为0的那一方就知道冲突了。

5.4 全局异常处理与统一返回

项目中写了一个@RestControllerAdvice全局异常处理器,捕获业务异常、参数校验异常和兜底异常。比如填报表单中workHours填了23.5,超过法定工时上限,业务层抛出的异常会被统一包装成Result.error("工时超出合理范围")返回前端。这样Controller里就不用散落一堆try-catch,代码整洁很多。

注意:异常提示信息一定要面向用户、说人话。后端程序员写日志时习惯输出“IllegalArgumentException”,但前端弹窗也这么显示,用户根本看不懂。这个项目里的所有异常提示都做了文案转化,操作体验完全不一样。

6. 前端Vue实现:从页面结构到数据可视化

前端这部分的工作量其实比后端还大。菜单、路由、权限控制、表单校验、图表展示,每一块都有可讲的东西。

6.1 前端项目结构与基础搭建

前端用的是Vue CLI创建的标准工程,目录结构大致如下:

src/ ├── api/ # 接口封装 │ ├── workLog.js │ ├── stats.js │ └── user.js ├── assets/ ├── components/ # 通用组件 │ ├── Sidebar.vue │ └── PageHeader.vue ├── router/index.js # 路由配置 ├── store/ # Vuex状态 ├── views/ │ ├── Login.vue │ ├── Dashboard.vue │ ├── workLog/ # 填报、审核页面 │ ├── project/ # 项目管理 │ ├── stats/ # 统计报表页面 │ └── system/ # 用户管理页面 └── utils/request.js # axios封装

utils/request.js里重点做了三件事:请求拦截器加Token、响应拦截器统一处理错误码、401时清空登录态并跳登录页。这是所有页面正常工作的基础。

6.2 路由守卫与菜单权限

Vue Router的beforeEach守卫检查是否存在Token,没有就跳转到/login。不过单纯靠“有没有Token”来判断登录态是不严谨的,Token过期了怎么办?我在响应拦截器里做了“静默处理”:当后端返回401时,清除本地登录状态,刷新页面前跳转登录页。同时路由的meta字段记录角色要求,在守卫里做二次验证:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.path !== '/login' && !token) { next('/login'); return; } if (to.meta.roles && !userStore.hasRole(to.meta.roles)) { next('/403'); return; } next(); });

侧边栏菜单是遍历路由表动态生成的,项目经理只能看到“项目进度”“审核”菜单,普通员工看不到“系统管理”。这样菜单本身和路由权限联动,不需要维护两份配置。

6.3 工作量填报表单的细节

填报页面用的是Element UI的el-form,比较关键的是两个组件选择:日期用el-date-picker限制不可选择未来日期;工时用el-input-number设置step=0.5、min=0.5、max=12。任务选择用级联下拉:先选项目,再选该项目下的任务。

这里有个交互上的坑:级联下拉如果数据量大,每次切换项目都要重新请求任务列表,很卡。我用了el-select的remote远程搜索,输入关键字再请求接口,既避免全量加载,又提高查找效率。如果你在写类似功能,可以直接抄这个思路。

6.4 图表展示与统计报表页面

统计页面用了ECharts。第一个图表是“个人月度工时柱状图”,数据格式是[{month: '2024-05', totalHours: 128}],前端按月份动态生成x轴。第二个是“项目投入饼图”,展示各项目的工时占比。第三个是“人员负荷雷达图”,展示一个成员在多个项目上的时间分配。

ECharts的坑主要在数据格式化上。后端返回的工时是decimal类型,前端拿到的其实是字符串,图表的数值轴需要parseFloat再塞进去。另外饼图的百分比是小数的形式,需要自己转成百分比字符串:

series[0].data = projectStats.map(item => ({ name: item.projectName, value: parseFloat(item.totalHours) }));

如果你不想自己写这些格式化逻辑,直接用图表库帮带的formatter回调也完全可以。只是要注意:金额、工时这类数据在图表里一定要统一单位,不要前台显示“128”,后台其实是“128小时”,月份和年份也要标注清楚。这种细节看起来小,却是报表页面专业与否的分水岭。

6.5 前端本地调试必装的工具

这里分享一个调试经验:Vue项目开发时,脑子里一定要有Vue Devtools这个插件。Chrome应用商店直接搜“Vue Devtools”安装即可,装完之后可以在控制台面板直接查看组件树、Vuex状态、路由信息。我见过太多人搞了半天,页面报错看不到具体原因,其实用Devtools一看数据流哪里断了就清楚了。还有一个常见问题是vue.config.js的devServer.proxy配置,开发时把/api前缀代理到localhost:8080后端服务,能避免跨域问题。

注意:如果你在Windows上开发,vue项目依赖安装失败是很常见的事。优先检查Node版本,然后把npm镜像源切换成国内镜像,能省下大量排队时间。

7. 打包、部署与线上问题排查实录

这部分我踩过不少坑,挑几个最有代表性的问题展开。系统本身是个前后端分离项目,部署方式也比较典型:后端打jar包、前端npm run build后丢到Nginx。

7.1 后端打包以及数据库迁移

SpringBoot项目用Maven打包:

mvn clean package -DskipTests

生成target/work-stat-system.jar。建议打包前检查一下application.yml里的数据库连接配置,确认是测试环境还是生产环境库。数据库迁移我用的比较简单——SQL脚本文件直接执行,没有引入Flyway这类版本管理工具。项目小的时候可以这样做,但如果你打算把这套系统作为长期迭代的基础,建议尽早引入Flyway,否则上线后改了字段没有记录,回头排查历史数据问题会非常痛苦。

7.2 jar包能直接改吗:反编译的思路

有人会问,拿到一个jar包能不能反编译回来改源码。开发阶段自己打包的源码都在,不存在这个问题;但如果是接手别人的项目,只有部署好的jar包,那确实需要反编译。常用工具是IDEA自带的反编译插件(Java Decompiler)或者jadx、cfr这类工具。把jar包的class文件拖进去,能还原出大部分代码,但注释、泛型信息、常量名会丢失,MyBatis的XML文件通常在resources目录下直接被读出来,比class更完整。我的经验是:反编译只能用于理解逻辑,别指望能还原成可维护的源码,最终还是要设法找到原始代码仓库。

7.3 MySQL连接常见错误:SSL和时区问题

项目里MySQL连接串配置如下:

jdbc:mysql://localhost:3306/work_stat?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai

useSSL=false是关键——如果MySQL版本较高,默认尝试SSL连接,而本地开发通常没有配置证书,会报SSL connection error。serverTimezone要显式指定,否则MySQL 8.x默认时区与本地不一致,时间字段查出来会差8小时。这两个参数我几乎每次搭新环境都要遇到,建议直接固定写在连接串里统一维护。

7.4 MyBatis查询出现的参数类型问题

一个高频报错是IllegalArgumentException: Mapped Statements collection already contains value——这是因为XML里的id和Mapper接口里的方法名没对上。还有一个很容易踩的坑是or和and的拼接,动态SQL里如果位置放错,生成的SQL逻辑会完全偏离预期。调试方式很简单:把MyBatis的日志级别改成DEBUG,控制台直接打印SQL和参数。

mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

看到实际执行的SQL,比对着XML猜靠谱一万倍。我排查过的很多“查不到数据”问题,最后都是参数没传进去或者类型不匹配,控制台日志一眼就能看出端倪。

7.5 Nginx部署前端的配置

前端打包后生成dist目录,Nginx配置里做两个关键设置:将根路径指向dist目录,并将/api开头的请求反向代理到后端服务:

server { listen 80; server_name your-domain.com; root /usr/share/nginx/html/work-stat/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

特别注意:当使用Vue Router的history模式时,要加一条try_files $uri $uri/ /index.html;,否则刷新非首页路由会404。如果你用hash模式,则没有这个烦恼,但URL会带#号,看着不够正规。项目里我用的hash模式,完全是因为Nginx配置不熟的人更不容易出问题,求稳优先。

7.6 全局过滤器与XSS防护

从热搜词能看到很多人关注SpringBoot全局过滤器处理XSS攻击的问题。这套系统在登录和填报接口上做了基本的XSS过滤,做法是写一个OncePerRequestFilter,包装HttpServletRequest,对请求参数中的<script>等标签做转义处理。实现并不复杂,但能挡掉大部分反射型XSS攻击。如果你处理的是文件上传场景,比如上传PDF,建议在上传接口单独校验文件扩展名和MIME类型,不要依赖全局过滤器覆盖一切——安全防护永远是多层,而不是靠一个过滤器打天下。

8. 项目跑起来的完整手记:建库、启动、验收

最后把这套系统从零启动的完整流程梳理一遍,方便你拿到源码后按部就班地跑起来。

8.1 初始化数据库

打开Navicat,新建数据库work_stat,字符集选utf8mb4。导入项目里的init.sql脚本,会建好全表结构和基础数据(管理员账号admin、演示用户、示例项目和任务)。这里提醒一下:字符集一定要选utf8mb4,而不是utf8,因为utf8mb4能完整支持emoji等四字节字符,避免某些特殊文本插入时报错。过程中如果遇到表导入失败,多半是SQL文件版本和MySQL版本不兼容,把SQL文件里的ENGINE=InnoDB DEFAULT CHARSET=utf8mb4这些行删掉再执行基本就好了。

8.2 启动后端

用IDEA打开后端工程,等待Maven依赖下载完成。修改application.yml中的数据库账号密码,然后运行WorkStatApplication.java的main方法。看到“Netty started on port 8080”的日志就说明启动成功。这时可以用浏览器访问http://localhost:8080/api/auth/login测试接口,返回JSON说明后端OK。

8.3 启动前端

前端工程目录执行:

npm install npm run serve

默认端口8080很容易和后端冲突,配置中我设置了前端跑在3000端口,也建议你单独改动vue.config.js里的port。浏览器打开http://localhost:3000,用admin账号登录,进入系统后先填几条工作量数据,再到报表页看统计结果,整体流程就通了。

8.4 验收清单

系统跑通后,建议做一遍这样的验收:

  1. 普通员工账号:只能看到自己的填报记录,不能看到别人数据。
  2. 尝试重复填报同一天同一任务:系统应提示已更新,而不是报错。
  3. 填报超过12小时:后端应拒绝并返回友好提示。
  4. 项目经理审核通过一条记录后,个人月报表里的总数应立刻更新。
  5. 直接用未登录状态访问/api/stats/personMonth:应返回401,前端自动跳转登录页。

这套验收流程走下来,基本能覆盖核心链路。如果你在实操中遇到其他的坑,欢迎留言交流,我会持续补充到项目文档里。

我个人在实际项目中使用这套工作量统计系统的大半年时间里,最大的体会是:工作时间填报的门槛一定要足够低,员工才愿意持续用。如果填一次要五分钟,两周后大家就开始找借口不填了。所以我的建议是,二次开发时优先做这两个优化方向:一是通过企业微信或钉钉机器人,每天下班前推送填报提醒;二是把日报和周报自动生成,省掉员工重复写汇报文档的时间。技术层面的路已经铺好,剩下的就是结合团队实际情况把使用习惯培养起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询