危险作业工作票全流程解析:从字段设计到审批流落地
简介这份《危险作业工作票》资料包含动火、高处、受限空间、临时用电、起重吊装等特殊作业的安全作业证模板面向化工、制造、建筑等企业的安全管理人员与现场监护人员。文档共1个doc文件压缩包整体约205KB体量精简但内容覆盖完整可直接用于日常作业许可填写与安全交底。文件从风险分析入手逐项列出动火作业的气体置换、高处作业的防坠落措施、受限空间的有毒有害气体防控、临时用电的漏电保护与一机一闸一保护等要点并配备审批签字栏便于企业落实层层确认责任。既能帮助企业规范特殊作业审批流程也可作为安全培训的案例教材已有86人学习参考适合需要完善作业票制度或进行现场安全管理的团队使用。1. 一张危险作业工作票把动火、高处、受限空间作业装进同一个流转现场最常见的情形是动火作业要单独申请动火证高处作业要单独开高处票受限空间作业又是另一套表格。三张纸长得差不多审批路径却不一样EHS 部门核对数据时只能靠人工翻纸质件漏一张就查不出来。所谓「危险作业工作票」就是把这些高风险作业统一成一张可流转、可留痕、可统计的作业凭证动火、高处、受限空间共用一套编号规则和审批框架只在作业条件确认、气体检测、防护措施这些环节按作业类型展开。对做信息化的人来说难点不在把表格搬进系统而在把三类作业的差异字段设计清楚——这张票的字段模型直接决定后续审批流、台账统计和到期提醒怎么做。本文按「字段设计 → 文档生成 → 状态流转 → 排错校验」的顺序把整条链路讲明白。2. 危险作业工作票的字段模型动火、高处、受限空间共用一套骨架差异集中在环境检测与防护措施2.1 三类作业共用的基础字段先确定编号和时间维度不管是动火、高处还是受限空间作业票的核心都围绕「谁、在哪、多长时间、谁批准、谁监护」。我把这部分定义为公共骨架数据库里直接做成一张主表作业类型只作为枚举字段存在。公共字段里最容易被忽略的是「作业结束时间」——它不只是用来展示后续的超期提醒和销票判断都要用它。字段设计如下字段名类型说明ticket_novarchar(32)作业票编号建议规则类型前缀 年月日 流水号work_typeenum动火 / 高处 / 受限空间applicantvarchar(50)作业申请人deptvarchar(100)作业单位locationvarchar(200)作业地点需精确到装置/楼层/房间号work_contenttext作业内容描述start_timedatetime作业开始时间end_timedatetime作业结束时间超期即失效supervisorvarchar(50)监护人同一时间只能监护一处作业approvervarchar(50)批准人statevarchar(20)待审批/已批准/作业中/已验收/作废2.1.1 为什么作业编号要带类型前缀动火票编号用MH高处票用GC受限空间票用SX。这不是为了好看而是当系统按编号做台账时可以直接用前缀拆分统计数据不需要额外关联作业类型字段。我一般会把前缀和流水号拆成两个字段显示时拼接排序时按流水号。下面的 JSON 是前端表单提交数据时的最小结构也是后续生成 Word 文档的数据来源{ ticket_no: MH-20250612-007, work_type: 动火, applicant: 张工, dept: 检维修车间, location: A 区换热站东侧管道, work_content: 焊接冷却水管道漏点, start_time: 2025-06-12 09:00, end_time: 2025-06-12 17:00, supervisor: 李工, approver: 王主任 }这段 JSON 里end_time与start_time的差值一般不超过一个工作日这是现场作业票的通行做法。动火作业涉及特级、一级、二级分级时work_type字段要扩展成work_typedanger_level两个字段分别存作业类型和危险等级便于按等级做审批权限控制。2.2 动火作业票特有的字段气体分析数据必须有时间戳动火作业的审批是否能通过很大程度取决于气体分析结果。经常有人把可燃气体浓度直接写成一个数字却忘了记录检测时间结果这张票在审查时没有任何说服力。气体分析数据应当固化成一维数组结构每次检测都是一条独立记录包含检测时间、检测位置、氧气浓度、可燃气体浓度、检测仪器编号。动火票里我额外增加这组字段{ gas_analysis: [ { check_time: 2025-06-12 08:40, check_point: 动火点 1 米范围内, oxygen: 20.9%, flammable: 0.2%, instrument_no: FX-105 } ], fire_measures: { fire_extinguisher: 配备 2 具 8kg 干粉灭火器, isolate_equipment: 已隔离相邻可燃介质管道 } }检测记录数组化的好处是支持多点多次复测。实际动火作业中中断超过一定时间再复工需要重新做气体分析如果字段设计成单个值就没办法记录多次复测的结果。2.3 受限空间作业证特有的字段准入条件要靠勾选式措施来保证受限空间作业证与动火票最大的区别在于受限空间更强调「先检测、后进入」以及进出的人数管理。除了气体分析记录受限空间作业票里必须有「作业人数」和「逃生措施」两个必填项。人数字段不只是一个数字它会与监护人数量做联动校验——按照常见做法受限空间内超过 3 人作业时现场必须增加一名专职监护人员。confined_space: { worker_count: 2, ventilation: 强制机械通风已启动, isolation: 进出口已加装安全锁具, rescue_plan: 救援三脚架与安全带已到位, entry_permit: 作业前 30 分钟内检测合格 }entry_permit这个字段是用来记录气体检测是否在进入前 30 分钟内完成很多事故复盘里都能看到「检测完两个小时才进入」的教训时间窗口一变数据就失效了。为了避免语义歧义这个字段我建议存储检测完成时间而不是存一个「是否合格」的布尔值合规性可从时间戳直接判断。2.4 高处作业证特有的字段高度分级与临边防护措施绑定高处作业的关键参数是作业高度它直接决定作业证的审批层级。常见分级是2 米至 5 米为一级高处作业5 米至 15 米为二级15 米至 30 米为三级30 米以上为特级。用代码来表达这种分级逻辑时不要把高度和等级拆成两个无关字段应当由高度计算并自动带出等级。def high_work_level(height: float) - str: if height 30: return 特级高处作业 if height 15: return 三级高处作业 if height 5: return 二级高处作业 if height 2: return 一级高处作业 return 不上票函数说明high_work_level接收以米为单位的作业高度返回对应的作业票等级。前端选完高度后立即回填等级后端存储时再校验一次防止用户绕过界面直接修改接口数据。超过 30 米的特级作业一般要由企业分管负责人审批这个字段写进票面后审批流引擎可以直接用它做跳转判断。高处作业的安全措施字段默认勾选「安全带已检查、脚手架验收合格、临边洞口已封闭」这些作为必选项不勾选不允许提交。3. 用 python-docx 把工作票数据渲染成 .doc 作业证替代手填的工程做法3.1 最小可运行的 Word 工作票生成脚本前面字段设计好了接着解决「出票」的问题。常见做法是让用户在网页上填完数据后后端生成一份可打印的 Word 文档。有人直接把 HTML 模板改成.doc后缀这样做在打印时经常出现样式错位文件打开时还会有格式警告。更可靠的做法是用 python-docx 程序化地构建表格再转换成.doc格式。下面是最小可运行的脚本from docx import Document from docx.shared import Pt from docx.oxml.ns import qn def set_font(cell, text, boldFalse, size10.5): cell.text para cell.paragraphs[0] run para.add_run(text) run.bold bold run.font.size Pt(size) run.font.name 仿宋_GB2312 run._element.rPr.rFonts.set(qn(w:eastAsia), 仿宋_GB2312) data { ticket_no: MH-20250612-007, work_type: 动火作业, dept: 检维修车间, location: A 区换热站东侧管道, work_content: 焊接冷却水管道漏点, start_time: 2025-06-12 09:00, end_time: 2025-06-12 17:00, supervisor: 李工, approver: 王主任 } doc Document() title doc.add_paragraph() title.alignment 1 # 居中 run title.add_run(危险作业工作票) run.bold True run.font.size Pt(16) table doc.add_table(rows4, cols4) table.style Table Grid rows [ (作业编号, data[ticket_no], 作业类别, data[work_type]), (作业单位, data[dept], 作业地点, data[location]), (作业内容, data[work_content], 监护人, data[supervisor]), (开始时间, data[start_time], 结束时间, data[end_time]), ] for i, row in enumerate(rows): for j, value in enumerate(row): set_font(table.cell(i, j), value) doc.save(danger_ticket.docx)脚本逻辑拆解先定义set_font函数统一设置单元格字体。这里有一个关键细节中文字体不能只设置run.font.name还必须通过w:eastAsia属性指定东亚字体否则生成的文档在 Windows 上打开会退回默认字体票面格式直接垮掉。接着用add_table创建 4 行 4 列的表格Table Grid样式确保打印时有实线边框。3.1.1 表格列宽设置的坑上面的脚本生成的表格四列等宽在打印时经常出现「作业编号」一列太宽、「作业地点」一列太窄的问题。实际出票时建议手动指定各列宽度。python-docx 里设置列宽要先取到当前表格的全部列再逐列赋值不能在创建后只设置一列widths [Cm(2.5), Cm(4.5), Cm(2.5), Cm(5.5)] for row in table.rows: for idx, width in enumerate(widths): row.cells[idx].width width这里Cm从docx.shared导入。每行逐一设列宽是为了避开某些 Word 版本里「仅最后一行生效」的渲染问题。票面总宽度要控制在页面可用宽度以内A4 纵向纸去掉左右页边距后可用宽度约 16 厘米四列加起来不要超出这个值。3.2 在作业票中插入气体分析检测表动火和受限空间作业票都需要气体分析区块这是普通作业票与危险作业工作票的重要区别。在焊接时气体数据表要能放多条检测记录便于现场复测后补录。我在生成文档时先根据gas_analysis数组的长度动态创建表格行数再逐行填充gas_table doc.add_table(rows1, cols5) gas_table.style Table Grid headers [检测时间, 检测位置, 氧含量, 可燃气体, 检测仪器] for i, h in enumerate(headers): set_font(gas_table.cell(0, i), h, boldTrue) records [ [08:40, 动火点 1 米内, 20.9%, 0.2%, FX-105], [08:55, 动火点 1 米内, 21.0%, 0.1%, FX-105], ] for rec in records: row gas_table.add_row() for col_idx, val in enumerate(rec): set_font(row.cells[col_idx], val) doc.add_paragraph(批准人签字__________ 监护人签字__________) doc.save(danger_ticket.docx)参数说明add_table(rows1, cols5)创建 1 行表头add_row()每调一次追加一行数据。签名区用add_paragraph直接写文本不要再用样式复杂的文本框普通段落打印效果最稳定。3.3 把 python-docx 生成的 docx 转成 .doc 格式python-docx 只能生成.docx而题目里的原始文件是.doc。许多老旧的作业票管理系统仍要求上传.doc格式。我一般用 LibreOffice 做格式转换命令如下soffice --headless --convert-to doc --outdir ./output ./danger_ticket.docx参数说明--headless表示不打开图形界面适合服务器定时任务调用--convert-to doc表示输出为 Word 97-2003 格式--outdir指定输出目录不写则默认输出到当前目录。依赖的soffice命令在安装了 LibreOffice 的 Linux 服务器上天然可用Windows 上需要把安装目录加入 PATH。转换完成后建议用 Word 打开验证一次排版重点看表格边框和字体是否保持原样。4. 危险作业工作票接入审批流状态机定义、JSA 风险关联与台账统计4.1 从申请到销票的状态机用代码锁住非法跳转危险作业工作票一旦打印出来状态就不能随意改动。比如已批准的票不能跳过「作业中」直接进入「已验收」作废的票更不能重新激活。我用一个状态枚举来表示流转节点from enum import Enum, auto from dataclasses import dataclass class TicketState(Enum): DRAFT auto() PENDING_APPROVAL auto() APPROVED auto() IN_PROGRESS auto() COMPLETED auto() VOID auto() def can_transit_to(self, target: TicketState) - bool: allowed { TicketState.DRAFT: {TicketState.PENDING_APPROVAL, TicketState.VOID}, TicketState.PENDING_APPROVAL: {TicketState.APPROVED, TicketState.VOID}, TicketState.APPROVED: {TicketState.IN_PROGRESS, TicketState.VOID}, TicketState.IN_PROGRESS: {TicketState.COMPLETED}, TicketState.COMPLETED: set(), TicketState.VOID: set(), } return target in allowed[self]这段代码的关键在allowed字典每一个状态列出可跳转的目标状态。can_transit_to在每次状态变更前调用不满足跳转规则直接拒绝并记录日志。作业票业务上有一个容易被忽视的点APPROVED到IN_PROGRESS的跳转可以由作业负责人执行但IN_PROGRESS到COMPLETED必须由监护人确认两者操作角色不同状态机之外还要绑定操作人权限。4.1.1 用 SQL 查询在票状态下做超期判断纸面作业票到期后没人收是现场管理的通病。电子化之后查询逻辑里加一个「未销票且已过期」的条件即可SELECT ticket_no, work_type, location, end_time FROM work_ticket WHERE state IN (APPROVED, IN_PROGRESS) AND end_time NOW();参数说明end_time NOW()直接把超期未结束的作业票捞出来。这条语句在每天早会上跑一遍把结果发给各车间安全员比人工翻票可靠得多。注意state只筛选APPROVED和IN_PROGRESS因为DRAFT状态影响范围很小COMPLETED已经销票不需要提醒。4.2 把 JSA 风险分析与危险作业工作票绑定作业票不应该孤立存在。动火作业开始前安全员通常需要对作业步骤做 JSA作业安全分析把每一步的风险和管控措施列出来再决定是否开票。我一般用一张关联表把 JSA 记录和作业票做多对一关联业务上更准确的说法是「一个作业点一次 JSA对应一张票」。CREATE TABLE jsa_record ( jsa_id INT PRIMARY KEY AUTO_INCREMENT, ticket_no VARCHAR(32) NOT NULL, step_name VARCHAR(100) NOT NULL, risk_desc VARCHAR(500) NOT NULL, control_measure VARCHAR(500) NOT NULL, risk_level VARCHAR(10) );建表说明ticket_no是外键指向work_ticket表。step_name是作业步骤描述比如「连接气瓶」「点火焊接」risk_desc写风险如「气瓶回火爆炸」control_measure写管控措施如「安装回火防止器」。设计关联表而不是把 JSA 直接塞进作业票主表是因为一个作业可能拆出七八个步骤塞进主表会导致字段爆炸查询也不方便。4.3 作业票台账统计按车间和作业类型计算月累计票量管理部门真正关心的是横向对比哪个车间动火票开得最多哪个作业类型出问题概率高。台账统计不需要复杂的 BI 工具一条标准 SQL 就能按月聚合SELECT dept, work_type, COUNT(*) AS ticket_cnt, SUM(CASE WHEN state COMPLETED THEN 1 ELSE 0 END) AS finished_cnt FROM work_ticket WHERE start_time BETWEEN 2025-06-01 00:00:00 AND 2025-06-30 23:59:59 GROUP BY dept, work_type ORDER BY ticket_cnt DESC;统计逻辑说明COUNT(*)统计当月开票总数SUM(CASE WHEN ...)统计其中已完成的数量两者的差值可以推算未按时销票的数量。BETWEEN比start_time ... AND start_time ...少写一个条件但要额外注意边界值23:59:59这种写法只适合精确到秒的场景。如果数据库是 MySQL 8.0可以考虑把start_time改成DATETIME(3)精确到毫秒避免边界隐患。5. 危险作业工作票落地时的排错清单与自动校验技巧5.1 生成的 .doc 打不开的常见原因排查很多工程师在服务器上生成作业票后发现文件传到 Windows 上打不开第一反应是代码写错了其实多半是文件损坏或路径问题。按下面这张表逐项排查现象原因处理方式docx 文件双击提示「文件格式和扩展名不匹配」实际内容是 HTML扩展名却是 docx统一用 python-docx 生成不要改扩展名soffice 转换后文件 0KB输出目录不存在或权限不足确认--outdir目录可写Word 打开提示需要修复docx 是 zip 包传输时被截断用python -m zipfile -t danger_ticket.docx验证包完整性中文字体变成方块服务器缺少中文字体安装 fonts-noto-cjk 或 Windows 字体排查逻辑里最值得留意的是第三行。python-docx 生成的.docx本质是 zip 压缩包如果服务器磁盘空间满了doc.save()可能没有报错但文件写了一半。用 zipfile 模块快速验证import zipfile try: with zipfile.ZipFile(danger_ticket.docx) as zf: zf.testzip() print(文件包完整) except zipfile.BadZipFile: print(文件损坏检查磁盘空间和写入权限)5.2 作业票有效期自动检测把超期票和证书过期一起筛出来危险作业工作票的超期问题分两层作业本身超过票面允许时间以及作业人员持有的特种作业操作证在作业期间过期。后者更难发现因为人证分离的现象在检修旺季很常见。我写过一个合并检查的脚本同时核对作业结束时间与持证有效期from datetime import datetime def validate_ticket(ticket, cert_data): issues [] now datetime.now() end_time datetime.fromisoformat(ticket[end_time]) if end_time now: issues.append(作业票已超过结束时间未完成销票) for person, cert in cert_data.items(): cert_expire datetime.fromisoformat(cert[expire_date]) if cert_expire end_time: issues.append(f{person} 的特种作业证在作业期间过期) return issues检查逻辑validate_ticket接收作业票数据和人员证书数据返回问题列表。第一层判断作业结束时间是否在当前时间之前第二层判断证书有效期是否覆盖整个作业窗口。举例电焊工的焊接与热切割特种作业证如果 6 月 11 日到期而作业票 6 月 12 日才结束这张票即使在审批时是合规的作业当天也已经变成「无证作业」。5.3 用一个技巧防止一位监护人同时监护多处作业动火、高处、受限空间三类作业现场最危险的状态就是一个人同时监护两个作业点。系统里常见的做法是加唯一性约束之外的业务校验因为数据库不能自己知道「监护人同一时段只能出现一次」。把下面的查询放到提交票证的接口里每次开票前跑一遍SELECT supervisor, COUNT(*) FROM work_ticket WHERE start_time 2025-06-12 17:00 AND end_time 2025-06-12 09:00 AND supervisor 李工 AND state NOT IN (VOID, COMPLETED) GROUP BY supervisor;这段 SQL 用区间重叠判断start_time 新票结束时间 AND end_time 新票开始时间是判断两个时间区间是否有交集的常用写法比写成BETWEEN更准确。如果查询结果大于 0说明这位监护人在这段时间已经绑定了其他作业票直接拒绝开票并提示更换人选。这类校验放在数据入口层做好比事后翻视频监控有效得多。本文还有配套的精品资源点击获取