
简介一套面向中小型建筑公司与包工头的工地记账系统PHP源码基于PHPMySQL构建将人、钱、项目三大核心要素纳入同一平台收入/支出双轨记账精确到分支持工程款项目关联、支出类别管理材料、人工、设备等8类并可实时计算净收益同时提供工人档案、日工资标准、项目全周期跟踪及自定义时间段的收支统计以条形图、饼状图直观呈现。包体共15个文件主要为9个PHP功能页面登录、仪表盘、收入支出、项目管理、报表等、1个SQL数据库脚本、前端Chart.js绘图脚本及统一CSS样式压缩包仅15KB部署轻量。已有68人学习下载。源码附带默认管理员账号admin/123456所有功能开箱即用且具有登录验证、会话管理、数据库事务保障及响应式界面支持根据实际业务二次开发工资结算、考勤打卡、材料库存等模块适合作为工程项目财务自记账或教学参考。1. 工地记账系统PHP源码现场记的每一笔都要月底能对上工地上最先乱的不是进度是账。包工头笔记本记一页、带班手机转一笔、材料员自己用一个Excel月底一对少则差几百多则差几千最后基本都是老板自己掏钱补窟窿。这套工地记账系统PHP源码就是把工人考勤工天、借支预支、材料进出、日常开支这四类流水统一收进一个PHP后台操作完直接落MySQL月底按人按项目汇总谁该结多少、谁提前借了多少一张表拉得出来。源码是全开源的没有加密授权、没有多余框架依赖改改数据库配置就能跑。适合三类人不想买付费SaaS的现场管理者想拿真实业务做二次开发的PHP从业者以及拿它当课程设计或毕业设计底子的学生。2. 先搭环境PHPMySQL跑通源码的三个关键动作2.1 为什么这套源码用原生PHP而不是框架先看这份资源的定位它是给工地现场和中小包工头用的部署环境大概率是一台轻量云服务器、一块虚拟主机或者直接Windows电脑装PHPStudy。选原生PHP不是技术落后是部署成本最低——不用Composer拉依赖、不用配伪静态、不用管框架版本兼容把文件传上去建好数据库就能访问。我拆过不少记账类源码凡是依赖框架的多半死在“框架要求PHP版本、服务器只给老版本”这一关原生PHP这套基本解压缩就能跑。这也是我向非专业运维的包工头推荐它的原因出了问题远程指导时只需要说“打开php.ini改一行”而不是让对方在宝塔面板里折腾Composer和扩展。2.2 目录结构四个目录管住代码和文件拿到源码压缩包后先别急着往服务器传按下面表格过一遍结构知道每个文件夹是干嘛的后面改东西才不迷路。目录/文件职责备注/config数据库连接配置、站点参数部署时唯一必须改的目录/install安装向导生产环境建议装完删除/admin后台管理页面登录入口在这里/action表单处理、数据写入记账逻辑的主要代码/templates页面模板想改界面样式只动这里/uploads票据图片上传目录需要写权限/sql建表语句与测试数据.sql用Navicat或命令行导入这个目录划分是典型的“入口-业务-模板”三层admin负责展示和收集表单action负责校验和写库templates只管HTML输出。把页面和逻辑分开改样式时不容易碰坏记账代码。我接手这套源码时会先读action目录因为工地记账系统的核心不是页面多好看而是每一笔流水有没有被正确写进MySQL。上传到服务器时还要注意两点uploads目录要给PHP运行用户写权限否则保存票据图片时直接500install目录装完后立刻改名或删掉防止有人访问安装向导把库洗了。2.3 部署三步建库、改配置、起服务第一步建数据库。打开Navicat或者命令行创建名为gongdi的库字符集选utf8mb4CREATE DATABASE IF NOT EXISTS gongdi DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE gongdi; SOURCE /你的绝对路径/sql/gongdi.sql;这里用utf8mb4而不是老项目最常见的utf8是为了让中文和生僻字都不乱码。SOURCE导入时如果报错多半是SQL文件里包含外键约束相关语句把报错行贴出来单独看不要整段跳过。导入成功后可以用SHOW TABLES;确认七张表都在。第二步改数据库配置。打开config/database.php填入真实的数据库账号密码?php // config/database.php return array( host 127.0.0.1, // 数据库地址本机就是127.0.0.1 port 3306, // MySQL端口默认3306 dbname gongdi, // 库名和第一步创建的一致 user root, // 数据库用户名 pass your_password, // 数据库密码 charset utf8mb4, // 字符集别改成gbk );这里的坑在pass字段密码里如果有$号用双引号包会触发PHP变量解析导致连不上库。我习惯全部用单引号包字符串并且密码直接复制不手敲。改完配置后可以先在命令行下执行一句php -f config/database.php看看有没有报错再进浏览器。第三步启动。本地开发用PHP内置服务器最快php -S 0.0.0.0:8080 -t public-t指定站点根目录0.0.0.0允许局域网内其他电脑访问这样工地上其他人也能用平板打开记账页面。启动后浏览器访问http://本机IP:8080/install按向导填管理员账号密码看到安装成功后再处理install目录。如果用的是宝塔面板或PHPStudy直接在站点设置里把运行目录指向public并加上伪静态规则即可。提示部署到公网服务器时一定要给配置文件设置只读权限并修改默认管理员密码别图省事用源码包里默认的admin/admin。2.4 数据库表结构七张表各管一段账装好后先别急着录数据把表结构过一遍比什么都重要。这套源码的核心表如下表名作用关键字段关联关系workers工人档案id, name, work_type, daily_wage被attendance/advance/settlement引用attendance工天记录id, worker_id, work_date, work_days, overtime_hours, status结算时被SUM汇总advance借支预支id, worker_id, amount, advance_date, settle_statussettle_status1表示已入结算materials材料库存id, name, unit, price, stock进出库时更新stockmaterial_log材料进出流水id, material_id, type, quantity, created_at与materials.stock对账expense其他开支id, category, amount, expense_date, note独立统计不参与工资结算settlement结算单id, worker_id, period, actual_pay, settled_at生成后不可随意修改这张表关系清楚了你对整个系统的理解就到位了。attendance记的是考勤流水advance记的是预支流水material_log记的是库存流水settlement是最终产物。七张表之间靠worker_id和material_id串起来没有任何一张表是孤岛。初始化完成后的第二件事是进后台系统设置把两个参数先定死一是记账周期按月结算、每月几号封账二是工人默认工种和日薪表。这两个参数后面所有结算都依赖。常见的错误是后补工种——工地都干了一个月才补上木工日薪350历史工天按哪个价格结就会扯皮。先定参数再录流水这是我拆这类项目反复强调的第一原则。3. 核心记账逻辑拆开看工天、借支、材料款怎么串起来3.1 工天登记与工资计算按日薪×工天再加工时工地场景不能照搬办公室的考勤机工人经常早上在A工地、下午去B工地还受天气影响停工。所以这套系统里工天不是一个固定考勤数据而是记账员每天收工后在后台按人补记的流水。每一条attendance记录包含工人、日期、工天数、加班小时、备注。answer看action/attendance_save.php里的核心函数?php function calc_worker_wages($worker_id, $start_date, $end_date, $db) { $sql SELECT w.daily_wage, w.work_type FROM workers w WHERE w.id ?; $stmt $db-prepare($sql); $stmt-execute(array($worker_id)); $worker $stmt-fetch(); $sql SELECT IFNULL(SUM(a.work_days), 0) AS total_days, IFNULL(SUM(a.overtime_hours), 0) AS total_ot FROM attendance a WHERE a.worker_id ? AND a.work_date BETWEEN ? AND ? AND a.status 1; $stmt $db-prepare($sql); $stmt-execute(array($worker_id, $start_date, $end_date)); $row $stmt-fetch(); $base $row[total_days] * $worker[daily_wage]; $ot $row[total_ot] * ($worker[daily_wage] / 8 * 1.5); return array( base $base, overtime $ot, total $base $ot, days $row[total_days] ); }逻辑说明先把工人表里的日薪取出来再统计周期内的总工天和总加班小时最后计算基础工资和加班工资。返回的数组里除金额外还带days字段方便调用方在结算单上同时展示出勤天数。参数说明daily_wage是日薪工地按日结或月结都适用$start_date和$end_date是结算周期的起止日期格式为YYYY-MM-DDovertime_hours是加班小时数1.5是加班倍率不同地区有差异直接改这个系数就行。注意两个查询都用预处理语句这里不能拼接字符串工地工人的姓名可能有生僻字参数化能避免SQL注入。3.2 借支预支的扣减避免月底倒欠工资工地记账里最容易起纠纷的是借支。工人月中找包工头借5000包工头在微信里记一笔月底结算时忘了工资照发钱打水漂。系统的做法是把借支单独成表结算时强制参与扣减。看action/settlement_save.php里的逻辑?php function build_settlement($worker_id, $period, $db) { $wages calc_worker_wages($worker_id, $period[start], $period[end], $db); $sql SELECT IFNULL(SUM(advance.amount), 0) AS total_advance FROM advance WHERE worker_id ? AND advance_date BETWEEN ? AND ? AND settle_status 0; $stmt $db-prepare($sql); $stmt-execute(array($worker_id, $period[start], $period[end])); $advance $stmt-fetch(); $actual $wages[total] - $advance[total_advance] - $period[other_deduct]; return array( period $period[label], worker_id $worker_id, wages_total $wages[total], advance_total $advance[total_advance], other_deduct $period[other_deduct], actual_pay $actual, ); }这个函数先调3.1的工资计算再把周期内未结算的借支SUM出来最后用应发工资减借支再减其他扣款得到实发金额。关键在advance表里的settle_status字段它默认是0表示这笔借支还没参与过结算结算单确认生成时要把周期内对应的借支批量更新为1。只要这个状态维护好了同一笔借支就不会被扣两遍。新手踩坑往往在这里直接改库把借支删了重录settle_status没复位结算单就漏扣了。我实际操作时会在借支页面顶部加一个未结算借支余额的汇总显示每次准备给工人结算工资前先按人查一遍这个数再生成结算单把逻辑错误挡在生成之前。3.3 材料进出库与库存联动超发出库直接从SQL拦下材料模块比工资模块更依赖事务。入库加库存、出库减库存如果减到负数还在记账后面盘库必定对不上。看action/material_log_save.php里的写入逻辑?php function material_stock_change($material_id, $type, $quantity, $db) { if ($type out) { $sql UPDATE materials SET stock stock - ? WHERE id ? AND stock ?; $stmt $db-prepare($sql); $stmt-execute(array($quantity, $material_id, $quantity)); if ($stmt-rowCount() 0) { throw new Exception(库存不足出库失败); } } else { $sql UPDATE materials SET stock stock ? WHERE id ?; $stmt $db-prepare($sql); $stmt-execute(array($quantity, $material_id)); } $sql INSERT INTO material_log (material_id, type, quantity, created_at) VALUES (?, ?, ?, NOW()); $stmt $db-prepare($sql); $stmt-execute(array($material_id, $type, $quantity)); }用UPDATE带stock ?条件是防超发的关键一行MySQL更新时如果库存不足影响行数为0代码抛出异常这笔出库被整体拦下。这里用SQL条件代替PHP里先查库存再判断的写法避免两个请求同时读到同一个库存数造成超发账面和实物才对得上。material_log表里每条流水都留了type和quantity之后对账只要把时间段内的进出流水SUM出来和materials.stock字段比对一致说明账和库存是闭环的不一致就是中间有人改过库存或者手动插过流水。这里我建议在出库页面把操作人字段做成必填谁发的料、发给哪个班组全部留痕后面扯皮时有据可查。4. 部署与记账常见问题排查五个最容易翻车的现场坑这五条是我在处理同类工地记账系统时踩过、也帮人排查过的真实问题按出现频率排序。前两条是环境问题后三条是数据习惯问题。4.1 PHP版本新了老代码反而报错现象用PHP 8.2或8.3跑登录页打开是白屏或者提示Deprecated: Using ${var} in strings is deprecated。原因源码里用了PHP 5.x时代的字符串拼接写法PHP 8把这些写法标记为弃用错误级别直接顶到页面。解决先在php.ini里屏蔽DEPRECATED级别error_reporting E_ALL ~E_DEPRECATED ~E_NOTICE display_errors On改完重启PHP服务看业务能否正常跑。如果还有error级别报错再查代码里的老函数优先把mysql_系列替换成PDO或mysqli。本地开发我直接用PHPStudy把版本切成7.4这是最省事的办法不跟代码较劲。4.2 导入SQL后中文全是问号现象后台能看到菜单但工人姓名、材料名显示为????。原因SQL文件本身是UTF-8编码但客户端连接数据库时用了latin1写入的数据已经变成乱码。解决命令行导入前先执行SET NAMES utf8mb4;Navicat导入时在高级选项里把编码选为UTF-8。已经导坏的库不要在乱码基础上改把表和SQL文件整理好后重新导入一次再清浏览器缓存刷新页面。4.3 两个人同时记账后保存的覆盖了先保存的现象保管员上午记了一笔入库包工头下午又录了一条出库上午那笔不见了。原因页面列表不是实时查询而是下拉时加载了整份表单数据提交时把整行覆盖了。解决优先用带WHERE id的UPDATE不要先DELETE再INSERT。如果源码本身就是整表刷新型写法那就在操作前把最近30天的流水导出发给操作人确认后再改。多人协作时给每个账号分配独立权限保管员只能动材料包工头只能动结算减少交叉覆盖。4.4 月底结算后发现借支漏扣现象工人工资已发现金结算单上没扣借支工人不认账。原因借支金额录到了备注字段里没有落到advance表的amount列或者结算单生成后借支才补录。解决把借支录入页面做成金额必填不允许只写备注就提交。同时检查结算单生成按钮背后的逻辑确认有没有把周期内借支批量标记为已结算这个UPDATE有没有执行可以在生成后马上打开结算详情页核对借支合计。如果源码里没做标记自己补一个数据库更新语句封装在settle函数里。4.5 数据没有备份误删了只能干瞪眼现象管理员想清测试数据手滑把attendance表清了一个月工资没法算。原因后台没有回收站机制DELETE语句执行后不可逆。解决在服务器上配定时任务每天凌晨自动备份全库mysqldump -uroot -p gongdi /data/backup/gongdi_$(date %Y%m%d).sql写成脚本后用crontab每天执行保留最近7天的备份文件。重要操作前再手动导出一份当天的SQL。我个人的习惯是把备份按钮做到后台首页显眼位置每次封账前强制点一次这个动作能挽回绝大多数误操作。5. 进阶把月度结算导出成Excel给项目经理一张能直接算钱的表5.1 导出一个带完整结算信息的CSV文件后台的结算单页面看数据没问题但项目经理要的不是看屏幕是拿一张表去签字。用PHP直接把结算结果导出为CSV?php header(Content-Type: text/csv; charsetutf-8); header(Content-Disposition: attachment; filenamesettlement_ . date(Ym) . .csv); $out fopen(php://output, w); fwrite($out, \xEF\xBB\xBF); // 加BOM头防止Excel打开中文乱码 fputcsv($out, array(工人姓名, 工种, 结算周期, 工天数, 应发工资, 已借支, 其他扣款, 实发金额)); while ($row $stmt-fetch(PDO::FETCH_ASSOC)) { fputcsv($out, array($row[name], $row[work_type], $period_label, $row[total_days], $row[wages_total], $row[advance_total], $row[other_deduct], $row[actual_pay])); } fclose($out);fputcsv会把每行按英文逗号拼好并加上换行Excel双击打开就能按列排序。那行BOM头不是玄学不加它Excel识别UTF-8会乱码WPS不一定乱但办公电脑里装的多数是Excel这一步必须留。文件名里的date(Ym)会生成类似settlement_202506.csv的按月命名月底导出直接归档不用手动改名。5.2 让这张表经得起签字导出后别直接发。我一般会把CSV打开把实发金额这一列拉个合计SUM和当月从银行取现的总额对一遍。对不上先查借支汇总这是整个系统里最容易被绕过的一环再查日期边界看有没有把上月底的流水算进来。确认无误后打印让工人签字按手印电子版存一份在uploads/backup目录纸质版拍照归档后续有任何争议都有原始凭证。这套源码我前后改过几轮最深的体会是记账系统翻车从来不是程序算错而是录入习惯问题——借支不落表、日期乱填、备注代替金额。从那以后我不管改哪套工地记账源码都强制走一遍“先备份SQL、再清测试数据、最后跑上个月真实数据试算”的流程算出来的数字和手工台账一致了才敢交付。希望帮到你。本文还有配套的精品资源点击获取