尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

河南省在建工程技术资料档案管理系统:数据模型、归档组卷与OCR检索

河南省在建工程技术资料档案管理系统:数据模型、归档组卷与OCR检索 简介河南省在建工程技术资料档案管理系统操作手册以PDF格式呈现面向建筑施工企业的档案录入人员、项目工程师与项目经理也适合需要熟悉工程资料归档流程的技术管理人员查阅。手册围绕资料收集、整理、录入到归档的完整链条展开重点讲清各方职责划分与操作规范帮助使用者解决案卷著录项填写不统一、组卷口径不一致等实际问题。整包仅含1个PDF文件约35KB便于在电脑或手机上留存查阅。目录依次覆盖职责说明、系统实施流程、具体资料录入操作、案卷与卷内各著录项填写规范、录入人员设置及工程技术资料组卷要求并附建筑工程技术资料目录清单对案卷号、密级、起止日期、部门、分类号、保管期限、卷内顺序号等字段逐条给出填写口径标注「★」的资料需挂接原文彩色扫描件。目前已有90人学习读者可据此对照项目实际厘清从预归档到正式归档的操作要点提高资料录入的规范性与检索效率。1. 从验收前找一份检验批说起河南省在建工程技术资料档案管理系统到底管什么验收前三天项目部最常出现的场景不是资料写不出来而是知道有这份资料但不知道在谁电脑里、在第几卷、页码对不对。河南省在建工程技术资料档案管理系统要解决的正是这个它不把资料当普通文件堆在服务器上而是从一份检验批记录、一张混凝土试块报告产生的当天就把它挂到单位工程—分部—分项—检验批的固定节点上同时记下编号、日期、责任人、监理签认状态。系统真正管的是四件事资料落在哪个目录节点、文件本体的元数据是什么、当前处于编制还是已归档状态、谁能改谁只能看。读这份操作手册落地的人分两类一类是资料员和项目总工按章节顺序把资料录进去另一类是做系统实施和二次开发的工程师需要把纸质用表、卷内目录、移交清单翻译成表结构和校验规则。前者关心这一步点哪里后者关心这一步背后落了哪些字段、校验拦在哪一层。后面几章按这个顺序展开。2. 在建工程技术资料档案管理系统的数据模型怎么定2.1 工程—案卷—卷内文件—元数据四层结构为什么比文件夹树好用很多项目最初就是用共享盘加文件夹做归档前两个月很顺到中期开始崩。原因是文件夹只有位置一个维度一份文件想同时属于3 号楼和混凝土工程和2024 年 5 月只能靠一层层的名字拼一旦有人改了目录名或者把文件拖走历史不可追溯权限也只能按目录粗粒度给。把分类维度和存储维度拆开用四层结构落库后面所有检索、统计、移交都从这里长出来。层级对应实体关键字段关系工程 / 单位工程project工程编号、工程名称、建设单位、开工日期、状态1:N案卷volume案卷号、案卷题名、编制单位、保管期限、密级1:N卷内文件document文件编号、题名、责任者、日期、起止页、份数1:N文件对象 / 元数据doc_meta格式、页数、哈希、OCR 文本、存储路径1:1分部、分项、检验批不单独建层级而是作为 document 上的分类码存在。这样做的原因是分部数量随工程类型变化大做成表会撑出十几层深的树查询和界面都会很难受做成两位分类码既能在卷内目录里排序又能当检索条件用。2.2 把卷内目录搬进数据库核心表的建表语句纸质卷内目录上的每一列都要在库里找到落点。下面这段 DDL 是常见做法字段名按实际系统习惯改即可关键是约束要立在库这一层而不是只写在页面校验里。-- 案卷表一卷对应一个可装订、可移交的归档单元 CREATE TABLE volume ( id BIGSERIAL PRIMARY KEY, project_id BIGINT NOT NULL, -- 所属工程 / 单位工程 volume_no VARCHAR(32) NOT NULL, -- 案卷号如 03-02-005 title VARCHAR(200) NOT NULL, -- 案卷题名来自卷内目录首行 archive_type VARCHAR(20) NOT NULL, -- 基建 / 监理 / 施工 / 竣工验收 retention VARCHAR(10) DEFAULT 永久, -- 保管期限永久 / 30年 / 10年 status SMALLINT DEFAULT 0, -- 0编制中 1待审核 2已归档 3已移交 created_at TIMESTAMPTZ DEFAULT now(), UNIQUE (project_id, volume_no) ); -- 卷内文件表卷内目录的每一行 CREATE TABLE document ( id BIGSERIAL PRIMARY KEY, volume_id BIGINT REFERENCES volume(id), doc_no VARCHAR(64) NOT NULL, -- 文件编号编码规则见 2.3 title VARCHAR(200) NOT NULL, -- 文件题名 author VARCHAR(64), -- 责任者落到具体单位或个人 doc_date DATE, -- 文件形成日期不是上传日期 page_from INT, -- 卷内起页 page_to INT, -- 卷内止页 copies SMALLINT DEFAULT 1, -- 份数 status SMALLINT DEFAULT 0, -- 与案卷状态联动 ocr_text TEXT, -- 扫描件识别结果 content_tsv TSVECTOR -- 全文检索列见 4.1 ); CREATE INDEX idx_doc_volume ON document (volume_id, page_from); CREATE UNIQUE INDEX uk_doc_no_current ON document (doc_no) WHERE status 2;几个字段值得单独说。retention用中文枚举而不是数字是因为移交清单直接按中文输出少一层映射少一次出错doc_date必须与上传时间分开扫描补录时两者能差半年uk_doc_no_current是部分唯一索引只约束未归档的记录归档后的历史版本允许同编号共存这正是 4.3 里版本留痕的基础。如果数据库用的是 MySQLTSVECTOR换成FULLTEXT或者干脆把正文同步到检索引擎。录入时最常见的失败是文件能传上去但归档按钮是灰的八成是doc_date为空或者volume_id还没绑定这一类问题在 5 章的自检脚本里会被提前拦下。2.3 文件命名与唯一编码让每一份资料自带检索路径文件名带中文不是错但拿中文文件名当主键一定会出事同名的检验批质量验收记录在一条线上能有几百份跨系统导出时编码还可能被截断。稳妥的做法是给每份资料生成一个可反解的英文数字编号原始中文题名只作为title字段保存。编码规则建议固定六段import re # 项目码6位 - 单位工程2位 - 分部2位 - 分项2位 - 流水号4位 - 版本号2位 PATTERN re.compile(r^[A-Z0-9]{6}-\d{2}-\d{2}-\d{2}-\d{4}-V\d{2}$) def build_doc_no(project: str, unit: int, branch: int, item: int, seq: int, ver: int 1) - str: 按固定位数补零保证字符串排序等于业务排序 return f{project}-{unit:02d}-{branch:02d}-{item:02d}-{seq:04d}-V{ver:02d} def parse_doc_no(doc_no: str) - dict: 反向解析用于按分部批量筛资料和生成卷内目录 if not PATTERN.match(doc_no): raise ValueError(f编号不符合规则: {doc_no}) p doc_no.split(-) return dict(projectp[0], unitp[1], branchp[2], itemp[3], seqp[4], versionp[5])流水号补四位、版本号补两位看起来是小事实际决定了排序和分页。补零之后字符串升序就是业务升序卷内目录不用再写自定义比较器。版本号放进编号而不是放在文件属性里好处是同一条记录的多次修订天然可区分V01和V02在库里是两行谁签的、什么时候替换的一目了然。反解析函数在批量导入时尤其有用清单里只给编号系统就能自动填出单位工程和分部减少人工选错。用PATTERN做前置校验时注意项目码允许大写字母和数字别只写\d{6}实际项目码里常带字母。导入十万条清单时建议先整表跑一遍正则再入库把不合规的行导成 Excel 退回比一条条弹窗提示高效得多。3. 从单份资料录入到批量归档组卷操作手册里最常翻的三步3.1 单份资料录入先校验元数据再上传文件顺序很关键。先填元数据、后传文件是为了避免文件已经落到对象存储、记录却因为字段缺失没写进去最后变成有文件没台账的孤儿文件。下面这些字段建议在页面上就设成必填别留给后端补。字段校验规则不通过的典型表现文件编号命中 2.3 的正则且当前状态下不重复提示编号已存在引导走版本升级文件题名非空长度 4–200只写记录表三个字后续检索无效责任者非空用简称而非全称移交时对不上形成日期不晚于当天不早于开工日期扫描补录时容易填成上传日所属案卷必须已选且案卷状态为编制中案卷已归档需先解锁文件格式PDF / OFD / JPG单个 ≤50MB手机拍的十几兆 JPG 会拖慢预览操作上还有一个细节扫描件在录入前先做一遍方向校正和去黑边再上传。系统的 OCR 对倾斜页面的识别率会明显下降这一步花两分钟后面检索能省很多翻页。3.2 批量导入Excel 清单加一个约定好的文件包目录一个分部做完往往积压几百份资料逐条录不现实。批量导入的关键不是接口快慢而是清单和文件包之间有一个谁都不能违反的对应关系文件包里的 PDF 以文件编号命名清单第一列也是文件编号。目录约定一旦定下来脚本就能自己找文件、自己对不上就报错。import pandas as pd from pathlib import Path BASE Path(/data/import/project_2024) # 导入包根目录 df pd.read_excel(BASE / 导入清单.xlsx, dtypestr) # 全部按字符串读避免编号被转成科学计数法 ok, bad 0, [] for _, row in df.iterrows(): doc_no row[文件编号].strip() src BASE / files / f{doc_no}.pdf # 文件包内一律用编号命名 if not src.exists(): bad.append((doc_no, 文件缺失)); continue size_mb src.stat().st_size / 1024 / 1024 if size_mb 50: # 超大文件先拆分或压缩再导 bad.append((doc_no, f体积超限 {size_mb:.1f}MB)); continue if not row[文件题名].strip(): bad.append((doc_no, 题名为空)); continue upload(volume_norow[案卷号], doc_nodoc_no, titlerow[文件题名], authorrow[责任者], doc_daterow[形成日期], filesrc) ok 1 print(f成功 {ok} 条失败 {len(bad)} 条) pd.DataFrame(bad, columns[文件编号, 原因]).to_excel(BASE / 导入失败清单.xlsx, indexFalse)dtypestr这一句别省Excel 里 12 位以上的数字编号经常被自动转成科学计数法读进来就成了1.23457E11后面所有匹配全错。失败清单一定要落盘成文件导完让资料员照着改比在控制台刷屏有用。3.3 归档组卷卷内目录生成与 PDF 合并案卷状态从编制中改成待审核之前通常要完成两件事卷内文件按页码顺序合并成一个整卷 PDF以及生成一份与页码完全对应的卷内目录。合并顺序必须由page_from决定不能依赖文件名的字母序。#!/usr/bin/env bash # 按卷内页码顺序合并归档 PDF页码存在库里这里用导出的序号文件名模拟 VOL/data/volume/03-02-005 ls $VOL/pages | sort -t- -k5,5n # 先确认序号顺序避免合并后页码错位 python - PY from pypdf import PdfWriter, PdfReader import glob, os files sorted(glob.glob(/data/volume/03-02-005/pages/*.pdf), keylambda p: int(os.path.basename(p).split(-)[4])) # 按流水号排序 w PdfWriter() cursor 1 for f in files: r PdfReader(f) w.append(r) # 逐份追加保留原始页 print(f{os.path.basename(f)}\t起页 {cursor}\t页数 {len(r.pages)}) cursor len(r.pages) with open(/data/volume/03-02-005/卷内文件.pdf, wb) as out: w.write(out) print(f合计 {cursor - 1} 页) PY合并后打印的起页信息就是卷内目录的原始数据直接回写数据库的page_from和page_to比手工数页码可靠。整卷 PDF 建议再做一次文本层检测如果卷内文件.pdf用文本搜索搜不到任何关键词说明之前上传的是纯图片需要回退到 OCR 流程补文本层否则检索模块在这卷上是空的。4. 检索、权限与版本让在建资料找得到、改得对、追得回4.1 图纸和扫描件的全文检索怎么落在建工程里超过一半的文件是扫描件和图纸纯靠文件名检索等于没检索。落地方案分两步上传后异步跑 OCR 把文本写进ocr_text再把title、doc_no、ocr_text合并进检索引擎。中文分词在多数数据库自带的全文检索里支持一般常见做法是外挂带中文分词插件的检索引擎或者在写入前用分词库处理一遍。-- 写检索列把编号、题名、识别文本合并编号用分词友好写法保留 UPDATE document SET content_tsv to_tsvector(simple, coalesce(doc_no,) || || coalesce(title,) || || coalesce(ocr_text,)) WHERE id :id; -- 查混凝土试块相关记录相关度优先、日期次之 SELECT doc_no, title, author, doc_date, volume_id, ts_rank(content_tsv, q) AS rank FROM document, to_tsquery(simple, 混凝土 试块) q WHERE content_tsv q AND status 2 -- 只看在建资料已移交的走另一套库 ORDER BY rank DESC, doc_date DESC LIMIT 50;simple配置不做词干还原对中文和编号混合的场景反而稳因为编号被切碎后就搜不到了。OCR 文本入库前做一次去空白和全角转半角能明显减少搜不到的投诉。检索结果一定要带上volume_id和卷内页码资料员拿着结果就能直接去翻纸质卷。4.2 角色权限矩阵与在建、归档状态机权限别写成一个个布尔开关撒在代码里按角色收敛成一张矩阵实施和运维都看得懂。角色录入修改元数据替换文件归档删除移交导出资料员是仅自己录入仅编制中否否否专业工程师是本专业仅编制中否否否项目总工是本项目待审核及以前是否否监理否审核意见否否否否档案管理员否否否是仅未归档是系统管理员否否否否审计后否状态机配合权限一起生效status2已归档之后document的更新接口直接返回 403而不是让前端把按钮置灰。前端的灰按钮挡不住直接调接口这一点在做验收演示时经常被忽略。4.3 版本留痕与签章冻结一份资料在签认之前可能改五遍签认之后再改就是事故。做法是把可改和已固定用一条明确的分界线切开签章动作完成的那一刻当前记录置为status2并写入frozen_at、frozen_by和文件哈希。之后再要修改只能新增一条V(n1)记录旧记录保留在库里。哈希值在这里是核心sha256存一份移交或调阅时重新算一遍比对能证明文件从签章到现在没被动过。前端展示时建议把版本历史排成时间线而不是只显示最新版否则出了问题连当时提交的是哪一版都说不清。5. 交付前的自检把操作手册里的检查项变成一条命令操作手册末尾通常附一页检查清单人工对着清单翻几百卷非常费时间也很容易漏。更稳的做法是把清单翻译成脚本在提交归档申请之前跑一次把问题清单导出来派活。下面这段是最常用的几项。import pandas as pd, re, hashlib REQ [doc_no, title, author, doc_date, page_from, page_to] PATTERN re.compile(r^[A-Z0-9]{6}-\d{2}-\d{2}-\d{2}-\d{4}-V\d{2}$) def sha256_of(path): h hashlib.sha256() with open(path, rb) as f: for chunk in iter(lambda: f.read(1 20), b): h.update(chunk) return h.hexdigest() def check(df, base_dir): issues [] seen {} for _, r in df.iterrows(): doc_no str(r[doc_no]) for f in REQ: # 必填字段 if pd.isna(r[f]) or str(r[f]).strip() : issues.append((doc_no, f缺少必填字段 {f})) if not PATTERN.match(doc_no): # 编号规则 issues.append((doc_no, 编号不符合六段规则)) if pd.notna(r[page_from]) and pd.notna(r[page_to]) and r[page_to] r[page_from]: issues.append((doc_no, 起止页倒置)) if doc_no in seen: # 同编号重复且都没有版本区分 issues.append((doc_no, f与第 {seen[doc_no]} 行重复)) seen[doc_no] r.name p base_dir / f{doc_no}.pdf if not p.exists(): issues.append((doc_no, 文件缺失)) elif r.get(file_hash) and sha256_of(p) ! r[file_hash]: issues.append((doc_no, 文件哈希与记录不一致)) return pd.DataFrame(issues, columns[文件编号, 问题]) # 用法读导出清单跑完直接生成整改工单 df pd.read_excel(/data/export/卷内清单.xlsx, dtypestr) out check(df, base_dir__import__(pathlib).Path(/data/volume/03-02-005/pages)) out.to_excel(/data/export/归档前自检问题.xlsx, indexFalse) print(out[问题].value_counts().to_string())几处参数值得按项目调整。REQ里如果有项目不强制要求的字段例如部分监理资料不填页号从列表里去掉即可别为了跑通脚本硬填假数据file_hash在早期库里可能为空判断前先做if r.get(file_hash)的保护否则会刷出一堆无意义的哈希不一致base_dir指向的是文件包目录而不是数据库文件包命名规则改了这里必须同步改否则缺失项会全量误报。最后一层校验靠不住的地方是页码连续性。库里page_from和page_to只保证不重叠、不倒置不保证中间不缺页。跑完上面的脚本后再按volume_id分组排序检查第 n 行的page_from是否等于上一行page_to 1不等就是缺页或者页码没回写这一条检查十分钟能写完赶上验收前能省一整晚。本文还有配套的精品资源点击获取
返回列表