- 添加工作时间配置数据库迁移(V12__add_work_hours_config.sql) - 创建WorkHoursCalculator工具类用于工作时间计算 - 添加工作时间配置API接口(SystemConfigController) - 修改后端服务使用动态工作时间(TaskService、TaskItemService、TaskApprovalService、AnalyticsService) - 在Settings.vue中添加工作时间配置UI - 修改前端时效格式化函数使用动态工作时长(TaskHall.vue、CreateTask.vue) - 所有时间计算(截止时间、延误扣分、时效格式化)现在使用配置的工作时间,替换硬编码的8小时
11 KiB
11 KiB
工作时间系统实施计划
一、需求概述
1.1 核心需求
- 工作时间配置:在系统设置中增加上班时间设置(开始时间、结束时间)
- 动态计算:一天的工作时间根据设置的上班时间段作为自然日(替换硬编码的8小时)
- 截止时间计算:计算截止时间时只计算到上班时间段内
- 延误扣分限制:延误扣分时间也需要限制在上班时间段内才扣分,超过上班时间不扣分
1.2 涉及场景
- 截止时间计算(任务创建、子任务分配时)
- 延误扣分计算(任务完成时、进行中任务检查时)
- 时效格式化显示(前端显示、钉钉通知)
- 子任务截止日期显示(前端任务卡片)
二、数据库设计
2.1 使用系统配置表(sys_config)
方案选择:使用现有的 sys_config 表存储工作时间配置(参考 site_url 配置的实现方式)
配置项设计:
config_key:work_hours.start_time(上班开始时间,格式:HH:mm,如 "09:00")config_key:work_hours.end_time(上班结束时间,格式:HH:mm,如 "18:00")config_group:systemvalue_type:stringbuilt_in:1(系统内置,不可删除)
默认值:
work_hours.start_time:"09:00"work_hours.end_time:"18:00"- 工作时间长度:9小时(9:00-18:00,不含午休)
2.2 数据库迁移文件
创建 V12__add_work_hours_config.sql:
-- 添加上班时间配置
INSERT INTO `sys_config` (`config_key`, `config_value`, `config_group`, `config_name`, `description`, `value_type`, `built_in`, `sort_order`, `create_time`, `update_time`, `deleted`)
VALUES
('work_hours.start_time', '09:00', 'system', '上班开始时间', '工作日上班开始时间,格式:HH:mm(24小时制),例如:09:00', 'string', 1, 20, NOW(), NOW(), 0),
('work_hours.end_time', '18:00', 'system', '上班结束时间', '工作日上班结束时间,格式:HH:mm(24小时制),例如:18:00', 'string', 1, 21, NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE `update_time` = NOW();
三、后端实现
3.1 工作时间计算工具类
文件路径:backend/src/main/java/com/designhub/util/WorkHoursCalculator.java
核心方法:
getWorkHoursConfig():从系统配置获取工作时间(开始时间、结束时间、工作时长)calculateDeadline(LocalDateTime startTime, BigDecimal workHours):计算截止时间(只计算工作时间段)calculateDelayHours(LocalDateTime deadline, LocalDateTime actualTime):计算延误时间(只计算工作时间段内的延误)formatWorkHours(BigDecimal hours):格式化工时显示(X天X小时,基于工作时间段)
计算逻辑:
-
截止时间计算:
- 从开始时间(接单时间)开始,累加工时
- 只在工作时间段内累加(跳过非工作时间)
- 如果当前时间不在工作时间内,先移动到下一个工作日的开始时间
- 如果工时跨越多天,跳过周末(可选,根据需求)
-
延误时间计算:
- 从截止时间到实际完成时间
- 只统计工作时间段内的小时数
- 跳过非工作时间(下班后、周末)
3.2 系统配置 Service 扩展
文件路径:backend/src/main/java/com/designhub/service/SystemConfigService.java
新增方法:
getWorkHoursConfig():获取工作时间配置(返回 WorkHoursConfig 对象)
新增内部类:
public static class WorkHoursConfig {
private LocalTime startTime; // 上班开始时间
private LocalTime endTime; // 上班结束时间
private int workHoursPerDay; // 每天工作小时数
}
3.3 需要修改的后端文件清单
3.3.1 时效格式化(替换硬编码的8小时)
TaskService.java:formatHours()方法TaskItemService.java:formatEstimatedHours()方法TaskApprovalService.java:formatEstimatedHours()方法
修改方式:
- 从
WorkHoursCalculator.formatWorkHours()获取格式化结果 - 使用动态的工作时长(从配置读取)替换硬编码的8小时
3.3.2 截止时间计算
TaskService.java:任务创建时的截止时间计算TaskItemService.java:子任务创建时的截止时间计算TaskApprovalService.java:任务审批通过后的截止时间计算
修改方式:
- 使用
WorkHoursCalculator.calculateDeadline()计算截止时间
3.3.3 延误扣分计算
AnalyticsService.java:getDesignerPerformance():设计师绩效统计中的延误扣分getDesignerDeductionDetails():设计师扣分明细中的延误扣分
DelayPenaltyService.java:calculateDelayPenalty()方法(如果有使用)TaskService.java:任务完成时的延误扣分计算(completeTask()方法)
修改方式:
- 使用
WorkHoursCalculator.calculateDelayHours()计算延误时间 - 只计算工作时间段内的延误
四、前端实现
4.1 工作时间配置 UI
文件路径:frontend/src/views/Settings.vue
新增标签页:在"网站设置"标签页中添加"工作时间设置"区域
UI 组件:
- 开始时间选择器(TimePicker,格式:HH:mm)
- 结束时间选择器(TimePicker,格式:HH:mm)
- 工作时间长度显示(自动计算,只读)
- 保存按钮
配置项:
work_hours.start_time:上班开始时间work_hours.end_time:上班结束时间
4.2 需要修改的前端文件清单
4.2.1 截止时间计算
frontend/src/components/TaskCard.vue:getSubTaskDeadline()方法frontend/src/views/TaskHall.vue:getSubTaskDeadline()方法frontend/src/views/CreateTask.vue:estimatedDeadline计算属性
修改方式:
- 从系统配置获取工作时间
- 使用工作时间计算截止时间(调用后端API或前端计算)
4.2.2 时效格式化显示
frontend/src/views/TaskHall.vue:formatEstimatedTime()函数frontend/src/views/CreateTask.vue:formatWorkHours()函数
修改方式:
- 从系统配置获取工作时长
- 使用动态工作时长替换硬编码的8小时
五、API 设计
5.1 获取工作时间配置
接口:GET /api/system-config/work-hours
响应:
{
"code": 200,
"data": {
"startTime": "09:00",
"endTime": "18:00",
"workHoursPerDay": 9
},
"message": "操作成功"
}
5.2 更新工作时间配置
接口:PUT /api/system-config/work-hours
请求体:
{
"startTime": "09:00",
"endTime": "18:00"
}
响应:
{
"code": 200,
"data": true,
"message": "操作成功"
}
5.3 计算截止时间(可选,如果需要前端计算)
接口:POST /api/system-config/calculate-deadline
请求体:
{
"startTime": "2026-01-14T09:00:00",
"workHours": 16
}
响应:
{
"code": 200,
"data": "2026-01-16T10:00:00",
"message": "操作成功"
}
六、实施步骤
阶段一:数据库和基础工具类
- ✅ 创建数据库迁移文件
V12__add_work_hours_config.sql - ✅ 创建
WorkHoursCalculator工具类 - ✅ 在
SystemConfigService中添加getWorkHoursConfig()方法 - ✅ 创建工作时间配置 API(
SystemConfigController或新 Controller)
阶段二:后端核心逻辑修改
- ✅ 修改
TaskService.formatHours()使用动态工作时长 - ✅ 修改
TaskItemService.formatEstimatedHours()使用动态工作时长 - ✅ 修改
TaskApprovalService.formatEstimatedHours()使用动态工作时长 - ✅ 修改任务创建时的截止时间计算逻辑
- ✅ 修改子任务创建时的截止时间计算逻辑
- ✅ 修改
AnalyticsService中的延误扣分计算 - ✅ 修改
TaskService.completeTask()中的延误扣分计算
阶段三:前端实现
- ✅ 在
Settings.vue中添加工作时间配置 UI - ✅ 创建前端 API 调用(
api/system.ts) - ✅ 修改
TaskCard.vue中的截止时间计算 - ✅ 修改
TaskHall.vue中的截止时间计算 - ✅ 修改
CreateTask.vue中的截止时间计算 - ✅ 修改前端时效格式化函数
阶段四:测试和优化
- ✅ 测试工作时间配置的保存和读取
- ✅ 测试截止时间计算(跨天、跨周末等场景)
- ✅ 测试延误扣分计算(工作时间段内、外)
- ✅ 测试时效格式化显示
- ✅ 测试钉钉通知中的时效显示
- ✅ 修复发现的 bug
七、技术难点和注意事项
7.1 工作时间计算复杂性
- 跨天计算:工时可能跨越多天,需要跳过非工作时间
- 跨周末:是否需要跳过周末?建议第一版先不考虑,只处理工作日的工作时间段
- 时区问题:确保使用系统时区
7.2 性能考虑
- 工作时间配置需要频繁读取,建议缓存(可以使用 Spring Cache)
- 截止时间计算可能涉及循环,需要优化算法
7.3 向后兼容
- 如果配置不存在,使用默认值(9:00-18:00,9小时)
- 确保旧数据不会因为配置缺失而出错
7.4 数据迁移
- 现有任务的截止时间是否需要重新计算?
- 建议:不重新计算,只影响新创建的任务和新的延误扣分
- 现有延误扣分记录是否需要重新计算?
- 建议:不重新计算,只影响新产生的延误扣分
八、测试用例
8.1 工作时间配置
- 测试保存和读取工作时间配置
- 测试配置格式验证(HH:mm 格式)
- 测试开始时间不能晚于结束时间
- 测试配置不存在时使用默认值
8.2 截止时间计算
- 测试同一工作日内完成的任务
- 测试跨天的任务(例如:16小时工时,从上午开始)
- 测试从非工作时间开始的任务(例如:晚上接单,从第二天早上开始计算)
- 测试跨周末的任务(如果实现)
8.3 延误扣分计算
- 测试在工作时间内延误的扣分
- 测试跨越下班时间的延误(只计算工作时间内的延误)
- 测试跨越周末的延误(只计算工作日的延误)
- 测试提前完成的任务(不扣分)
8.4 时效格式化
- 测试小时数少于1天的情况
- 测试小时数等于1天的情况
- 测试小时数超过1天的情况
- 测试小数点小时数(如 1.5 小时)
九、预计工作量
- 数据库设计:0.5 天
- 后端工具类开发:2 天
- 后端逻辑修改:3 天
- 前端 UI 开发:1 天
- 前端逻辑修改:1.5 天
- 测试和修复:2 天
总计:约 10 个工作日
十、风险点
- 计算逻辑复杂性:工作时间计算涉及复杂的日期时间处理,容易出现边界情况
- 性能问题:如果计算逻辑复杂,可能影响性能
- 向后兼容性:确保现有功能不受影响
- 数据一致性:确保前后端计算逻辑一致
十一、后续优化建议
- 支持多时间段:支持上午、下午两个时间段(考虑午休)
- 支持周末配置:允许配置周末是否工作
- 支持节假日配置:支持配置节假日,跳过计算
- 历史数据重算:提供工具重新计算历史任务的截止时间和延误扣分