很多项目经理以为甘特图里的“往后拖一格”只是视觉平移。但在企业版 project_enterprise 里,只要任务之间存在依赖,拖动一个任务,系统就必须重新判断后续任务还能不能在原来的缓冲区里待着。consume buffer 和 maintain buffer 的区别,就在这里爆发出来。
主要参考:
enterprise/project_enterprise/models/project_task.pyenterprise/project_enterprise/views/project_task_views.xmlenterprise/project_enterprise/static/src/*
一、级联重排先不是写库,而是先求一组可能日期
_web_gantt_move_candidates() 在真正提交结果前,会先准备旧值、用户时区、候选任务集合、有效工时区间,再进入一轮计算。这说明企业版把甘特拖拽当作“约束求解”而不是“直接写字段”。
这非常重要。因为只要有依赖链,后续任务是否能挪、往哪挪、需不需要报错,都不是当前任务自己说了算。
二、_get_new_dates() 解决的是“给定可用区间,任务还能落在哪”
这个方法会在向前或向后搜索的方向上,遍历有效工时区间,累计时长,直到满足候选任务持续时间。它既能给任务求新开始/结束日期,也能在 maintain buffer 模式下求缓冲区的新边界。
也就是说,buffer 不是一个抽象概念,它在源码里会被当作真正需要占用工作区间的时间长度去计算。
三、maintain buffer 比 consume buffer 更保守
_web_gantt_update_next_candidates_dates() 的逻辑里,如果不消耗 buffer,系统会尝试保留原本任务间的缓冲时长:它不是只把后续任务顺延到前置任务结束之后,而是还要把中间空出来的工作时间也重新算进去。
这正是两种策略的根本区别:
- consume buffer:允许挤掉原先的缓冲;
- maintain buffer:把缓冲也当作计划的一部分继续保留。
项目团队如果不理解这个差别,就会以为“为什么我只拖了一下,后面一串任务全变了”。其实系统不是失控,而是在忠实执行你选的排程策略。
四、为什么依赖排程不能只看任务自己
后续候选任务是否能移动,还要看:
- 任务本身是否仍是候选记录;
- 用户或公司是否存在足够可用区间;
- 依赖方向是前推还是后推;
- 同一批候选任务之间是否存在冲突。
这也是为什么企业版代码里会维护 first_possible_start_date_per_candidate、last_possible_end_date_per_candidate 这类结构。甘特重排本质上是在求一个满足依赖约束的可行解集合。
五、新手误区
- 以为甘特条拖动一定是一对一影响。实际上依赖链一长,后续会级联变化。
- 以为 buffer 只是视觉空白。源码里 buffer 是会被重新计算的工作区间。
- 以为 maintain buffer 只是“更慢一点”。它本质上是在保护原计划冗余。
六、落地建议
- 项目经理培训时要讲清两种 buffer 策略,不然大家会把系统输出当 bug。
- 在多人协作项目里,先保证 assignee 日历可靠,否则级联求解很容易出现看似奇怪的顺延。
- 对关键路径任务,优先使用能被团队共同理解的策略,而不是让每个人自己猜“拖动后会发生什么”。
七、结论
project_enterprise 的甘特重排并不是“移动任务条”,而是围绕依赖、工时区间和缓冲策略重新求解一组可执行日期。理解 consume buffer 与 maintain buffer 的区别,才算真正理解企业版甘特为什么会给出不同的级联结果。
DISCUSSION
评论区