工程项目最怕的两件事:进度不准和沟通不顺。一个靠谱的进度管理板块,不只是填表格,而是把“现场进展 → 计划对比 → 预警与纠偏 → 汇总分析”这整套闭环给系统化、可量化。本文目标是用接地气的口吻,给出完整思路、可落地的架构、业务流程、开发技巧,以及四个必须的代码示例(项目日进度填报、项目周进度计划、项目月进度计划、项目总进度计划),方便拿去直接改造或快速二次开发。
本文主要内容
工程项目部管理系统:把项目管理中常见的“计划、任务、进度、成本、质量、安全、变更、文档”模块系统化,供项目经理、技术负责人、现场人员、业主等使用。 进度管理板块的定位:承接日常进度填报(现场)、中短期计划编制(周/月)、总体计划(合同/里程碑),并完成对比、偏差分析与自动预警,最终输出给看板与报表。

简化三层架构:移动端/PC前端 → API 网关 / 后端服务 → 数据库 + 时序/缓存 + 消息队列
[现场移动/PC] [管理端PC]
\ /
\ (REST/GraphQL)
\ /
[API Gateway (Auth, Rate Limit)]
|
[Progress Service] --- [User/Perm Service]
|
-----------------------------------
| | | |
PostgreSQL Redis Kafka/Rabbit 文件存储(OSS)说明:
-- projects 表(略)
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
start_date DATE,
end_date DATE,
created_at TIMESTAMP DEFAULT now()
);
-- tasks/WBS 节点
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
project_id INT REFERENCES projects(id),
parent_id INT NULL, -- WBS
name TEXT NOT NULL,
planned_start DATE,
planned_end DATE,
planned_duration INT,
created_at TIMESTAMP DEFAULT now()
);
-- progress_entries:现场填报(支持日、周、月、总)
CREATE TABLE progress_entries (
id SERIAL PRIMARY KEY,
project_id INT REFERENCES projects(id),
task_id INT REFERENCES tasks(id),
entry_type VARCHAR(10) NOT NULL, -- 'day','week','month','total'
period_start DATE NOT NULL,
period_end DATE NOT NULL,
progress_percent NUMERIC(5,2) NOT NULL, -- 0-100
work_done TEXT,
blocker TEXT,
reported_by INT, -- user id
reported_at TIMESTAMP DEFAULT now(),
version INT DEFAULT 1 -- 乐观锁
);
-- indexes
CREATE INDEX idx_progress_project ON progress_entries(project_id, entry_type, period_start);现场人员填写日进度 → 本地保存草稿(可离线) → 提交到后端(API) → 后端校验并入库 → 发出流水线消息触发统计任务 → 若偏差/阻塞触发预警 → 项目经理在管理端查看/签批/下达整改任务
编制人草拟计划 → 提交审批(项目经理) → 审批通过后发布为“计划版本” → 各任务负责人接收并据此执行 → 每日上报与计划对比
创建合同里程碑计划 → 里程碑执行中同步并汇总周/月实际数据 → 产生合同履约报告与风险提示
说明:下面给出后端 Node.js (Express) + PostgreSQL (pg) 的示例 API,以及前端 React 的简洁组件示例。代码为示例,可直接套用。
// server.js (简化)
const express = require('express');
const bodyParser = require('body-parser');
const { Pool } = require('pg');
const app = express();
app.use(bodyParser.json());
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// 简单错误包装
function asyncHandler(fn){ return (req,res,next)=>fn(req,res,next).catch(next); }
module.exports = { app, pool, asyncHandler };// routes/progress.js (片段)
const express = require('express');
const { pool, asyncHandler } = require('../server');
const router = express.Router();
/**
* POST /api/progress/day
* body: { project_id, task_id, period_date, progress_percent, work_done, blocker, reported_by, client_idempotency_key }
*/
router.post('/day', asyncHandler(async (req, res) => {
const { project_id, task_id, period_date, progress_percent, work_done, blocker, reported_by, client_key } = req.body;
if (!project_id || !task_id || !period_date) return res.status(400).json({ error: 'missing fields' });
// 幂等:尝试插入到 idempotency 表(简化)
const client = await pool.connect();
try {
await client.query('BEGIN');
// 插入进度条目
const insertSql = `INSERT INTO progress_entries
(project_id, task_id, entry_type, period_start, period_end, progress_percent, work_done, blocker, reported_by)
VALUES ($1,$2,'day',$3,$3,$4,$5,$6,$7) RETURNING *`;
const r = await client.query(insertSql, [project_id, task_id, period_date, progress_percent, work_done, blocker, reported_by]);
// 推送异步消息(伪)
// await publishToQueue('progress.created', r.rows[0]);
await client.query('COMMIT');
res.json({ ok: true, entry: r.rows[0] });
} catch(e){
await client.query('ROLLBACK');
throw e;
} finally { client.release(); }
}));
module.exports = router;
周计划保存为计划版本,支持审批字段。
// POST /api/plan/week
// body: { project_id, week_start, week_end, items: [{task_id, planned_percent}], created_by }
router.post('/plan/week', asyncHandler(async (req,res) => {
const { project_id, week_start, week_end, items, created_by } = req.body;
const client = await pool.connect();
try {
await client.query('BEGIN');
const planRes = await client.query(`INSERT INTO progress_plans (project_id, plan_type, period_start, period_end, created_by) VALUES ($1,'week',$2,$3,$4) RETURNING id`, [project_id, week_start, week_end, created_by]);
const planId = planRes.rows[0].id;
const insertItemSql = `INSERT INTO progress_plan_items (plan_id, task_id, planned_percent) VALUES ($1,$2,$3)`;
for (const it of items) {
await client.query(insertItemSql, [planId, it.task_id, it.planned_percent]);
}
await client.query('COMMIT');
res.json({ ok:true, plan_id: planId });
} catch(e) {
await client.query('ROLLBACK'); throw e;
} finally { client.release(); }
}));
月计划与周计划类似,但增加审批流。
// POST /api/plan/month/submit
// body: { project_id, month_start, items: [...] }
router.post('/plan/month/submit', asyncHandler(async (req,res) => {
const { project_id, month_start, items, created_by } = req.body;
const client = await pool.connect();
try {
await client.query('BEGIN');
const planR = await client.query(`INSERT INTO progress_plans (project_id, plan_type, period_start, period_end, created_by, status) VALUES ($1,'month',$2,$3,$4,'draft') RETURNING id`, [project_id, month_start, getMonthEnd(month_start), created_by]);
const planId = planR.rows[0].id;
for (const it of items) await client.query(`INSERT INTO progress_plan_items (plan_id, task_id, planned_percent) VALUES ($1,$2,$3)`, [planId, it.task_id, it.planned_percent]);
// 提交审批:更新状态并通知审批人(伪)
await client.query(`UPDATE progress_plans SET status='pending_approval' WHERE id=$1`, [planId]);
// notify approver...
await client.query('COMMIT');
res.json({ ok:true, plan_id:planId });
} catch(e){ await client.query('ROLLBACK'); throw e; } finally { client.release(); }
}));
前端月计划(提示:支持导入 Excel)
总计划通常由项目经理/合同管理填写,带里程碑与关键路径。
// POST /api/plan/total
// body: { project_id, milestones: [{name, planned_date, description}], created_by }
router.post('/plan/total', asyncHandler(async (req,res) => {
const { project_id, milestones, created_by } = req.body;
const client = await pool.connect();
try {
await client.query('BEGIN');
const r = await client.query(`INSERT INTO total_plans (project_id, created_by) VALUES ($1,$2) RETURNING id`, [project_id, created_by]);
const pid = r.rows[0].id;
for (const m of milestones) await client.query(`INSERT INTO total_plan_milestones (plan_id, name, planned_date, description) VALUES ($1,$2,$3,$4)`, [pid, m.name, m.planned_date, m.description]);
await client.query('COMMIT');
res.json({ ok:true, plan_id: pid });
} catch(e){ await client.query('ROLLBACK'); throw e; } finally { client.release(); }
}));
前端总进度看板建议使用甘特图 + 里程碑组件,支持关键路径高亮。
给老板/PM看的 KPI(可量化):
用户体验点:
在这里我给大家推荐一个业务人员就能够直接上手的高性价比、零代码平台——简道云工程项目部管理系统,简道云背靠国内BI龙头帆软,在数据处理、数据展示上的能力有绝对优势,数据分析支持高度自定义,任何分析需求都可以快速制作仪表盘,简道云工程项目部管理系统让懂项目的人做应用,真正0代码,一线施工人员无需额外培训,小白快速上手。

FAQ 1:如何保证现场日进度填报数据的真实性与及时性?
现场数据真实与否,技术上可以做很多辅助,但不能完全替代管理制度。建议结合:
1)移动端拍照/视频 + 时间戳与 GPS(可选,但注意隐私合规);
2)扫码/电子签名(现场负责人签字确认);
3)随机抽查与现场巡检制度把控;
4)把进度填报纳入班组绩效考核,形成制度约束;
5)系统端为每次填报保留审核与退回记录,便于追溯与奖惩。
技术上要保证幂等性和离线草稿功能,降低现场上报阻力,提高填报率,从而提高时效性与数据覆盖面。
FAQ 2:当实际进度落后计划时,系统应该如何帮助决策与纠偏?
系统的责任不是替代决策,而是把问题可视化并提供决策所需的信息。建议实现:
1)自动偏差计算(百分比与滞后天数),并将偏差按风险等级分类;
2)关键路径识别,把对项目期影响最大的任务单独高亮;
3)提供纠偏建议模板(例如:增加班次/调整任务依赖/临时调配资源),供项目经理快速选择并下发任务;
4)建立“整改闭环”功能,从发现问题、下达整改、执行、复核直到关闭,且每一步都在系统中留痕;
5)必要时触发高层通知,支持多级预警。这样既能快速响应,也保证执行可追踪。
FAQ 3:我们的项目有大量离散小队,如何保证系统适配移动端并降低培训成本?
面对大量一线人员,用户体验比功能复杂度更重要。建议:1)优先做移动端(App 或 PWA),界面极简、流程短(目标是 1 分钟内完成日填报);2)使用模板填报(常见作业项一键选择),默认带入任务与照片模板,减少输入;3)提供中文/少数民族语支持(如需要);4)上线前做现场试点,挑选 2-3 个队伍做 1 周试运行,收集痛点快速迭代;5)培训采用“现场短训+视频+流程卡片”结合的方式,而不是长篇文档;6)系统提供操作日志与帮助中心,便于二次学习。技术上要支持低网速、离线草稿自动重试、并在后台进行输入校验以减少无效提交。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。