从"给人看的 Excel"到"能入库的规范行":点检表数据清洗逻辑拆解

2 阅读27分钟

从"给人看的 Excel"到"能入库的规范行":点检表数据清洗逻辑拆解

车间每天产出的是纸质点检表的电子版——供人阅读、签字、归档。项目要的是在线可管理的账票:结构稳定、字段整齐、能按设备追溯。这两者之间的落差,就是数据清洗要解决的问题。

本文拆解这套清洗逻辑:它怎么认出表头、履历和点检项目,怎么决定数据装进哪张表,认不出来时又怎么让人知道。相关实现基于AI办公助手程序,连同界面代码约 7,500 行,纯本地离线运行。

一、先定管理目标:四类对象,一套编码

动手解析 Excel 之前,先要回答的是"数据进库以后怎么管"。围绕这个目标,管理上先定了四件事。

一个 Sheet 就是一张点检报表。 点检表的组织单位是"表"而不是"文件":一份 Excel 里有几个 Sheet,数据库里就有几条报表记录,一一对应。

点检项目挂在报表下面。 报表回答"这是哪台设备、哪种频次的哪张表",项目回答"这张表要检哪些点"。层级固定,不会出现没有归属的项目。

修改履历单独留痕。 谁在什么时候改了什么,一行一条单独存,不和报表主体混在一起;"改过几次"能直接查出来,不必翻历史文件。

实际点检数据另表存放。 在线填报后产生的实测值属于业务运行数据,与"表框架"分开管理,清洗只处理后者。

1.1 串起来的是 report_code,不是三个编码

这里有一个容易说错的点,值得先纠正:三张表各有自己的自增主键(check_report_id / check_project_id / change_record_id),但跨表关联一律不走主键,统一走 report_code。

点检项目表里还有 check_code(EIRITEM + 日期 + 编号),修改履历表里还有 change_code——它们各自是"业务编号",用于对外标识和外部系统对接,不承担跨表关联。把三者说成"三个编码各自串联一条线"是错的,真实的连接只有一条:报表是主,项目和履历都靠 report_code 挂上去。

由此形成三条管理口径,后面所有识别规则都往这里对齐:

  • 模板统一:一套表单结构适配多产线、多设备;
  • 口径统一:同名字段在全系统保持同一含义(这条落地时踩过坑,见 5.5.2 的表头污染);
  • 留痕统一:任何改动都能回答是谁、何时、改了什么。

二、清洗的终点与两条硬约束

2.1 三张表的产出关系

表角色关联方式每次清洗产出
dv_check_report点检报表(主表)report_code 是该报表的唯一码;靠 machinery_code 与设备关联每个 Sheet 1 行
dv_check_project_info点检项目report_code 指向报表;另有 machinery_id + machinery_id_type(1 车间 / 2 产线 / 3 设备 / 4 工程)M 行
dv_check_change_record修改履历report_code 指向报表N 行
点检记录表记录实际点检数据—不在清洗范围

完整字段清单见附录 A。

2.2 两个推论

第一,表头字段取不全,设备就关联不上。 machinery_code 是连向设备表的钥匙;钥匙丢了,后面所有数据都挂不上设备。所以表头识别的精度排在所有规则的第一位。

第二,一张点检表只对应一个编码。 多个 Sheet 各自独立成表。该语义已由使用方确认,因此"10 个文件、29 个 Sheet"产出 29 行报表记录属正常语义,不是缺陷。

2.3 两条硬约束

纯本地离线。 清洗链路不调用任何网络或 AI 服务,词典与规则全部在本地。现场环境不能联网,这条没有商量余地。

Excel 不外发。 任何诊断信息都必须在"不泄露业务内容"的前提下,足以定位问题。这条约束直接决定了第八节的设计。

三、难点:不在算法,在表格没有结构约束

点检表由人填写、供人阅读,天然带着大量"便于阅读却不利于解析"的特征。数据库要的是另一种东西:字段固定、类型明确的规范行。中间的翻译就是清洗,难点集中在四条:

  1. 表头位置不固定:可能在第三行、第五行,也可能到第三十二行才进入点检项目区;
  2. 标签和值不在相邻列:实测设备编码的取值位置相对标签偏移 +6 列;
  3. 同一个概念有多种写法:改订内容 / 修订内容 / 变更内容 / 修改履历 / Change Log;
  4. "没有"和"没认出来"长得一样:表里本来没有履历区,和程序没识别出履历区,输出完全相同。

3.1 第四条最要命

它意味着:打开结果表看到空白时,你无法判断是这张表本来就没填,还是程序没看懂。

因此这个模块的设计目标不是"聪明",而是可解释、可诊断——每一步判断都能说明依据,判断错了能被记录下来、被人抓出来。后文所有设计(尤其是第六、八节)都服务于这个目标。

3.2 现场 Excel 的九个实测特征

特征实测表现对解析的影响
多版本同类点检表存在多个排版版本行列位置不能写死
多 Sheet10 个文件共 29 个 Sheet每个 Sheet 独立成一条报表记录
表头漂移第 3 行、第 5 行,甚至第 32 行才进入点检项目区必须先定位表头再取值
合并单元格项目、区分列大量纵向合并空白格要靠向下继承补
标签与值不同列machinery_code 相对标签偏移 +6 列同列右邻取不到值
说明区与图例"点检OK:○ 点检NG:×"、签字栏、备注容易被当作数据行
概念多写法改订内容 / 修订内容 / 变更内容 / 修改履历 / Change Log单一关键词覆盖不全
页脚页码1/3 形式的页码报表名需与页脚拼接
大段空行样例表自带 14~28 行全空行边界容易被空行打断

后两行会在后面反复出现:页码决定报表名能否拼对(5.5.1),空行决定履历边界停在哪(5.5.2)。

四、解法:六步流水线

上传(单文件 / 多文件 / .zip)
   │
   ├─① 读表        header=None 原样读入,跳过名字以 Sheet 开头的页
   ├─② 表头元数据  关键词 + 向右偏移 → line_code / machinery_code / product_no …
   ├─③ 修改履历    找表头行 → 定数据区 → 列映射 → 6 个标准列
   ├─④ 点检项目    三模式探测(日常 / 条件 / 周度)→ 项目 + 基准 + 方法 + 频度
   ├─⑤ 表角色映射  决定哪个 DDL 装哪类数据(语义优先,非位置)
   └─⑥ 导出        一个 xlsx,每个建表语句一张 Sheet(N 张,不固定 3 张)

第①步刻意不做"智能推断" :以 header=None 原样读入,避免 pandas 自行认定表头;并跳过名字以 Sheet 开头的页,排除模板附带的空白页。

4.1 失败即空表:一个刻意的选择

①~④ 任一步认不出来,输出空表,但不报错。

目的是让一张坏表不拖垮整批文件。代价很直接——"表里没数据"和"程序没认出来"在结果上无法区分。这个缺口只能靠过程记录补(第八节),这也是为什么记录不是一个可选项。

4.2 第⑤步:表角色映射曾出过一次严重错位

早期实现按位置映射:第 1 张 DDL 装主表、第 2 张装履历、第 3 张装点检项目。而用户实际上传的 DDL 顺序是 report → project_info → change_record,第 2、3 张正好对调——

结果是点检项目和修改履历互相装错表,字段名对不上,两张表同时全空。用户看到的现象就是"点检项目一个内容都没有,修改履历也没有"。

现在改为语义三轮判定:表名关键字 → DDL 字段特征 → 位置兜底,并把判定依据写进过程记录:

dv_check_project_info(idx1) → project_info(依据:表名含 project_info)
t_c(idx2)                   → change_record(依据:字段含 change_content)
x2(idx1)                    → change_record(依据:第 2 张表位置兜底)

五、三层识别:词典、位置、边界

整套识别没有使用任何机器学习,是三层启发式规则叠加的结果。

5.1 第一层:词典

默认配置 DEFAULT_CONFIG 中维护五张词表:

词表作用示例
header_keywords表头字段生产线 / 机械番号 / 制品品番 → line_code / machinery_code / product_no
履历表头(代码内 11 种)定位履历区改订内容 / 修订内容 / 变更履历 / Change Log / Revision
revision_column_keywords履历列年月日 / 担当 / 确认 / 承认
check_item_column_keywords点检项目列点检项目 / 水准 / 方法 / 频度 / № / 区分 / 表示
section_markers.end_markers结束边界签字 / 确认 / 承认 / 批准(项目侧另有 点检者 / 监督者)

匹配方式是去空格后的子串匹配:"点检 项目"也能命中"点检项目",用于应付人工敲入的不规则空格。

5.2 第二层:位置

值主要靠位置找,而不是靠列名。三处偏移各不相同:

位置向右扫描说明
表头元数据 extract_module1_header1~12 格取得标签右侧第一个非空值
表单键值对 _scan_form_pairs1~5 格扫 25×50 区域,构建"标签 → 值"字典
报表字段兜底 _build_report_data1~15 格兜底扫描,范围最大

偏移量会写进过程记录(如 machinery_code+6)。偏多少,直接反映这张表的排版有多不规矩。

5.3 第三层:边界

场景边界规则
修改履历从表头下第一个非空行开始,遇到第一个整行全空就停;或遇到"签字 / 确认 / 承认 / 批准"(且该格不超过 10 字);都找不到时取起点 +50 行
点检项目遇到"签字"、单元格精确等于结束词、或含"点检者 / 监督者"就停

点检项目侧特别强调"精确等于":若按子串匹配,"目视确认"这类正常内容会被当成结束词,数据区被提前截断。

5.4 为什么不用 AI

四条理由都是工程约束,不是偏好:

  • 离线:现场不能联网;
  • 确定性:同一张表跑两次必须得到相同结果,否则无法回归、无法审计;
  • 可解释:入库数据出问题时要能回答"这个值来自哪个单元格";
  • 可维护:规则写在 JSON 里,换产线改配置而不改代码。

代价是规则的边界就是识别的上限——词表没覆盖的表达一律认不出。这也决定了优化路径:不是换模型,而是把"认不出"变成可枚举、可逐个修掉的问题。

5.5 三个模块的识别细节

5.5.1 表头元数据
扫描前 20 行 × 前 80 列
  命中关键词 → 向右 1..12 格取第一个非空 → 字段值
  字段名含 "code" 的自动转大写
报表名:前 10 行找含「点检表 / 检查表 / 日常点检 / 周点检 / 月度点检 / 日点检」的单元格
       再用正则 \d+/\d+ 抓页脚(如 1/3),拼成「报表名1/3」

具体到某张表:machinery_code 的标签与值相隔 6 列,程序按规则右扫 12 格,在第 6 格取到值,并把 +6 写进记录。

报表行字段有三条来源,优先级 ③ > ② > ①:

序号来源说明
①extract_module1_header关键词 + 向右偏移
②_scan_form_pairs扫描 25×50,"标签 → 右邻 1..5 格"
③场景 JSON 的 search_keywords直接覆盖 ② 的标签映射

这条优先级对排障很实用:改了 header_keywords 却不生效,多半是被场景配置的 search_keywords 覆盖了。这是排查字段缺失的第一步。

5.5.2 修改履历
① 全表扫 11 种履历表头写法 → 记录所有命中的表头行(支持多张履历表)
② 起点:表头行下 4 行内第一个有任意非空值的行
③ 终点:第一个整行全空 / 命中结束词 / 兜底 +50 行
④ 列映射:表头行的关键词 → 6 个标准列(内容 / 日期 / 担当 / 确认 / 承认 / 序号)
⑤ 位置兜底:一个列名都没命中时,按列位置硬套 0..10

第 ⑤ 步是整个模块最危险的设计。 它让程序"总能给出答案",包括错误的答案——它会把"确认"的数据当成"承认"装进库,而且不报错。目前用 W_M2_POSITION_FALLBACK 单独标注,供人工复核。

这里还踩过一个更隐蔽的坑:表头行被当成表单扫描。表头是 改订内容 | 年月日 | 担当,按"标签 → 右邻"取值时,"改订内容"的右邻正好是"年月日",于是 content 字段取到了 年月日——29 行报表记录全被污染。修法是先划出表头区域,区域内不参与取值;右邻本身还是表头词就继续往右找,找不到宁可留空。

全表扫描并允许多张履历表,是为了适配同一 Sheet 内分段记录多次修订;而边界只认"第一个整行全空",表里本身含大段空行时就容易截早——这条与"删除空行"规则天然冲突,详见第九节。

5.5.3 点检项目:三种模式按固定优先级探测
顺序判定条件提取方式
1前 40 行出现 项目: / 项目:日常表:逐格扫描,序号自增,向下 1~3 行找"点检方法"
2同一行同时出现 表示 与 频度条件表:列映射 + 合并单元格继承,拼成 项目,表示为:X(1/W段取时),频度强制改写为 1/W
3出现 点检项目 表头周度表:按列关键词映射,item_no / check_item_type 向下继承

三种模式的差异不只在触发条件,也在数据组织方式:日常表以 项目: 为锚点逐格扫描,方法常写在项目名下方而非同一行;条件表依赖"表示"与"频度"同行出现,需列映射并用合并单元格继承补全层级,最后把多个字段拼成一句文本;周度表走标准表头映射,用向下继承补偿合并单元格造成的空值。

这里出现的 item_no、check_item_type、check_frequency 是清洗中间列名,入库时还要再映射到 content、judgment_criterion、cycle 等 DDL 字段。

条件表的"强制 1/W"是已知硬编码,后果是无法从数据反推真实周期。这也是后来把条件表固定按周处理的原因(见第六节)。

六、场景与表单类型

场景 = 一套可覆盖 DEFAULT_CONFIG 的配置 + 一个 report_type。界面下拉里的场景,就是 cleaning_scenarios/*.json 的文件名。

场景report_type配置形态
日常点检表1全量(12 个键,可逐项改规则)
周度点检表2精简(只写名字、说明、类型,其余回落默认)
月度点检表3精简
年度点检表4精简
条件点检表2(固定按周)全量(列拼接规则依赖配置)
安全装置点检表3(与月度同号)精简

配置形态的差异有明确理由:周、月、年三张表与日常共用同一套默认提取逻辑,只差一个 report_type;若每种都写全量,以后改默认规则就要改四处,迟早漏改。条件表相反,它的"区分 + 项目 + 表示"拼接逻辑依赖具体配置项,不能靠默认值兜底。

选错场景不会静默通过。 清洗时拿报表名与所选场景比对:报表名含"条件"却选了非条件表、或写"周度"却选了月度,都会记一条 W_SCENARIO_NAME_MISMATCH。比对前先剔除"2026年"这类日期片段,避免把年份误判成年度表。

七、批量处理与失败隔离

  • 输入:单文件、多文件,或一个 .zip(可含子目录);
  • 安全阀:单包 ≤ 500 条目,解压总量 ≤ 512MB,单条目 ≤ 128MB;../ 与绝对路径一律拒绝;
  • 合并:每个文件各自清洗,结果按表纵向 concat(ignore_index);
  • 隔离:某个文件解析失败就跳过,记 E_FILE_FAILED,其余继续;
  • 输出:一个 xlsx,Sheet 数 = 建表语句数,而不是固定 3 张;即使某张表 0 行,也写一张只有表头的 Sheet,保证这一约定永远成立。

八、过程留痕:一份能外发的"体检报告"

Excel 不能外发,过程记录是唯一能被带出去分析的东西。它不装业务内容,只装判断依据。

每次清洗在 output/ 下生成 清洗记录_<时间戳>.md,并覆盖 清洗记录_最新.md,五段结构:

段内容
一、总览文件数、成功数、失败数、ERROR/WARN 数、场景、DDL 顺序、规则开关、每个文件的识别健康分
二、逐文件明细每个 Sheet 的三模块识别过程:表头命中关键词及单元格坐标、字段命中率、位置偏移量;履历的表头行、匹配词、数据区、边界原因;点检项目的判定模式及依据;产出行数;表角色及判定依据
三、结构诊断30×30 类型码结构线框图、行画像、列画像与数据区统计、边界细节、关键词近似匹配、健康分构成
四、问题清单按严重度排列,每条带代码、文件、Sheet、说明
五、机器可读同内容的 JSON,便于脚本统计"哪类识别最常失败"

8.1 结构线框图:不泄露内容也能传递结构

       1  2  3  4  5
  1    T                    ← 标题行
  5    T  T  T  T  T        ← 履历表头行
  7                         ← 让它停下的空行
  8       T  D              ← 边界之后还有数据 → 截早了

不打开原表,光看这张图就能判断"表头占几行、数据区从哪开始、空行断在哪、边界是否截早",而不会看到任何品番、设备名或项目内容。这是保密场景下一个可复用的技巧。

8.2 问题代码:把"猜错了"变成可枚举的条目

18 个代码按类别分八组:

类别代码
表头E_M1_NO_HEADER、W_M1_LOW_HIT_RATE
履历E_M2_NO_HEADER、W_M2_EMPTY、W_M2_POSITION_FALLBACK、I_M2_END_FALLBACK
点检项目E_M3_NO_HEADER、W_M3_EMPTY、W_M3_FORCED_FREQ
边界与一致性W_BOUNDARY_TOO_EARLY、W_HEADER_ROW_DRIFT、W_REPORT_NAME_MISMATCH
表角色I_ROLE_BY_NAME、I_ROLE_BY_FIELDS、I_ROLE_BY_INDEX、W_ROLE_DUPLICATE
配置与场景W_REPORT_TYPE_INVALID、W_SCENARIO_NAME_MISMATCH、W_TABLE_COUNT_LT3
运行与文件E_PARSE_EXCEPTION、E_ENGINE_EXCEPTION、E_INPUT_NOT_FOUND、E_SCHEMA_EMPTY、E_NO_SHEET、E_FILE_FAILED、E_EXPORT_FAILED
质量W_LOW_HEALTH_SCORE、W_TEXT_KEPT_RATIO_HIGH

8.3 健康分:先看哪个文件最可疑

健康分 = 表头字段命中率 × 40
       + 履历有产出 × 20
       + 点检项目有产出 × 25
       + 未使用位置兜底 × 15
(< 60 🔴,60~80 🟡,> 80 🟢)

多文件批量时先看 🔴 的,不必逐个打开表排查。

8.4 脱敏:白名单式,而不是按行放行

保留原文掩码
① 词典词覆盖该格 ≥60% 字符,且该格不是"标签:值"形态(如 改订内容)其余全部:T(长度) / N / D / .
② 关键词近似候选 top3(如 变更内容)
③ 报表名单元格
④ 疑似表头窄行整行保留(默认关闭)

第 ④ 条为什么关掉,有实测依据:日常点检表的"标签行"里天然混着值,开启整行保留后,会把"详见工序一览表"(制品品番的值)、"主冷却电动水泵"(制品名的值)写进记录。结论是任何按行放行的规则都会漏,所以只做单元格级白名单。

每份记录还会给出"原文保留 N/M 格(X%)",超过 30% 就记 W_TEXT_KEPT_RATIO_HIGH,提示外发前须人工复核。上线时做过 619 条原文可追溯复检(凡出现在记录里的原文,必须能追溯到白名单格 / 词典词 / 报表名),无法追溯的为 0 条。

九、最难的七种情况

真正卡住人的不是写代码,而是下面这些判定上的两难。逐条列出应对与代价,因为代价往往比方案更需要提前说清。

情况卡在哪应对代价
履历区"没有"与"没认出来"两种情况输出同样为空表空表照出,但写 E_M2_NO_HEADER / W_M2_EMPTY 与边界原因结果表本身仍看不出区别,必须读记录
位置兜底误判一个列名都没命中时按位置硬套 0..10记 W_M2_POSITION_FALLBACK,列入人工复核兜底仍会产出"看起来正确"的错误数据
表头区被当成表单"改订内容"右邻是"年月日",content 取到"年月日"先划表头区域,区域内不参与取值;右邻仍是表头词就继续右找表头区域划错时错误向内传导
空行把边界截早样例表自带 14~28 行全空行;删空行规则(R1)开启后履历从 2 行变 7 行R1 默认关闭,记 W_BOUNDARY_TOO_EARLY边界仍需人工确认是否截早
模糊匹配命中多个候选"工程№"与词典"工程名"相似度仅 0.67近似候选只写进记录,不入库、不自动选词表漏项仍要人工补词
表角色错位按位置映射时第 2、3 张 DDL 对调改语义三轮判定,并把依据写进记录表名和字段都不具特征时仍需位置兜底
批量中单表异常一张坏表可能拖垮整批失败隔离,记 E_FILE_FAILED,其余文件继续被跳过的文件需单独重跑

这些情况背后是同一个结论:启发式识别不会主动告诉你"我猜错了",必须自己把猜测依据记下来。

十、怎么证明它是对的

讲逻辑容易,证明逻辑没跑偏才是工程量的主要部分。这套实现用四层验证兜住。

第一层:逐单元格回归。 5 个样例表在改动前后各跑一次,把三张表的每一格(类型 + 值)快照下来逐格比对。规则改动必须做到 diff = 0,只允许出现"有意变更"的字段(例如 report_type 从 1 改成 2)。"看起来对"不算验收。

第二层:真实数据复核。 用客户实际的 10 个文件跑,修复前后对比同一张表的关键列非空计数:

表修复前修复后
dv_check_project_info2 行,content / method 全空9 行,content / method / check_code / cycle_type 各 9 个非空
dv_check_change_record5 行,change_content 全空2 行,change_content / change_time 各 2 个非空
dv_check_report.content年月日作成:2021年07月28日 修订:2023年08…
表头污染单元格290

第三层:多种 DDL 顺序。 同一批数据分别用三种 DDL 顺序(用户真实顺序、原顺序、把表名改成 t_a/t_b/t_c 只保留字段特征)跑,三张表的数据归属必须完全一致。

第四层:端到端。 打包成 exe 后启动、拿到 HTTP 200、关窗口进程退出并释放端口;这些都要有实测输出,而不是"应该没问题"。

十一、效果与局限

这套逻辑已能覆盖日常、周度、月度、年度、条件、安全装置六类表单的常规排版:表头能定位到设备编码,履历与点检项目按各自模式拆成行,批量处理有失败隔离与安全阀,每次清洗都留下一份可外发的诊断记录。

但边界也很清楚:

  • 安全装置点检表挂的是默认词典,没有真实样例,表头写法一旦不同,识别就会偏;
  • 月度、年度场景未用真实月检、年检表验证,目前只是把 report_type 下发对了;
  • 词表未覆盖现场所有表达,"工程№"与"工程名"相似度 0.67 就是一例;
  • 条件表频度被硬编码为 1/W,业务上暂按周处理,无法从数据反推周期;
  • 多 Sheet 即多报表,这是已确认的业务语义,不算缺陷。

十二、接下来怎么做得更好

改进按优先级排定,都是可以落地的具体动作,而不是"引入模型":

级别内容状态
P0让失败可见:自检告警、结构化诊断、静默异常落日志、空表必须带原因已完成
P1魔法数字可配:扫描范围、偏移区间、结束词下沉到场景 JSON,边界改为"连续 N 行无数据才停"进行中
P1条件表周期不再硬编码,按配置推断周 / 月待做
P2标准清洗能力:文件指纹去重、质量报告 Sheet、清洗血缘 manifest、契约测试待做
P2词表扩充与近似候选闭环,把"工程№"这类漏项补回词典待做

十三、三个可迁移的结论

如果把这件事抽象一层,有三条结论对其他"表格转数据"的场景同样成立:

  1. "没数据"和"没认出"必须能被区分。 这是可观测性的起点,也是所有后续优化的前提——分不清这两件事,改规则就是盲改。
  2. 位置兜底是最危险的设计。 它让程序"总能给出答案",包括错的答案。兜底可以有,但必须留下痕迹、必须可被人工推翻。
  3. 按行脱敏不可靠。 只要表里有"标签和值混排"的行,任何按行放行的规则都会漏。要脱敏就做到单元格级白名单,并给出"保留比例"这样的可度量指标。

附录 A:三张表完整字段

A.1 dv_check_report

字段含义类型 / 取值
check_report_id主键bigint,自增
report_code文件编号、业务唯一码varchar(64);格式 check + 年月日 + 序号(000001)
report_name表头名称varchar(64)
line_id / line_code / line_name产线 ID / code / 名称bigint(NOT NULL)/ varchar(64) / varchar(64)(NOT NULL)
engineering_code / engineering_name工程 code / 名称varchar(64)
machinery_code / machinery_name设备番号 / 设备名称varchar(64)
product_no / product_name制品品番 / 品名varchar(64)
report_type报表类型int:1 日、2 周、3 月、4 年
line_type车间树类型int:1 车间、2 产线、3 设备、4 工程
content报表修订内容varchar(255)
recording_method记录方式varchar(300)
object_product对象制品varchar(64)
status启用状态char(1):1 启用、0 停用,默认 1
start_time / end_time启用开始 / 停用结束datetime
chart_show模板显示bigint:1 日、2 周、3 月、4 年、5 折线
sort / del_flag排序 / 删除标志int / char(1):0 存在、2 删除

其余字段是三段签字的 ID 列(report_create_id / report_confirm_id / report_admit_id 等)与 create_by、create_time、update_by、update_time。

两点需要注意:其一,report_code 在 DDL 注释里同时被定义为文件编号,兼有"手编辑、自动生成"两种来源,既是关联键也是对外编号——本文只用它做关联,不从它反解日期。其二,chart_show 在报表侧是"模板显示类型"(含折线),在项目侧却是 0/1 开关,同名不同义,映射时不能混用。

A.2 dv_check_project_info

字段含义类型 / 取值
check_project_id主键bigint,自增
report_code所属报表varchar(255),注释为"统一表头唯一码"
machinery_id / machinery_id_type关联对象 ID / 类型bigint / int:1 车间、2 产线、3 设备、4 工程
content点检项目varchar(255)
judgment_criterion判定标准varchar(255)
method点检方法varchar(255)
cycle_type周期类型int:1=1/始、2=1/终、3=1/值、4=日、5=周、6=月、7=年
cycle周期varchar(255)
input_type录入类型int:1 单选、2 数值、3 文本
option_value选项 / 上下限varchar(255),逗号分隔,格式随 input_type 变化
unit / length / decimals / default_value单位 / 长度 / 小数位 / 默认值varchar / int / int / varchar
important_id / classify_id重点符号 / 分类(备用)varchar(255)
check_code项目自动编码varchar(255),EIRITEM + 日期 + 编号
enable启停int:1 启用、0 停用
need_photo / need_video(含 _num)是否照相 / 录像及数量int:0 停用、1 启用
file_url作业指导书、参考图片varchar(255),逗号分隔的 ID
start_time / end_time生效 / 结束datetime

其余字段是 sort、chart_show(0 不显示 / 1 显示)、remark、del_flag 与审计四件套。

这张表信息量最大,有三处结构需要单独说明:cycle_type 是枚举而 cycle 是文本,两者并存说明周期既要能被机器判定,也要人能读;option_value 用逗号数组承载选项或上下限,单选类型按 [key,value,1,key,value,0] 重复,数值类型则是"下限、预知下限、预知上限、上限"四组符号与数值;need_photo / need_video 说明点检不只填数字,还可能要拍照留证。这三点决定了项目行不能被当成几列普通字符串。

A.3 dv_check_change_record

字段含义类型 / 取值
change_record_id主键bigint,自增
report_code所属报表varchar(64),注释为"标识"
change_time修改日期datetime
change_content修改内容varchar(64)
change_code / change_add修改编号 / 修改定位varchar(200) / varchar(64)
change_create_id / _name / _time担当人及其确认时间bigint / varchar(64) / datetime
change_confirm_id / _name / _time确认人同上
change_admit_id / _name / _time承认人同上
remark / del_flag备注 / 删除标志varchar(64) / char(1):0 存在、2 删除

履历与主表是同一套签字模型:change_create / change_confirm / change_admit 三组人与时间,对应主表的 report_create / report_confirm / report_admit。清洗时定为 6 个标准列,正是为了对齐这套结构:内容 → change_content、日期 → change_time、担当 → change_create_name、确认 → change_confirm_name、承认 → change_admit_name。

A.4 关联与来源

关系连接条件基数
报表 → 项目dv_check_project_info.report_code = dv_check_report.report_code1 : N
报表 → 履历dv_check_change_record.report_code = dv_check_report.report_code1 : N
项目 → 车间树machinery_id + machinery_id_typeN : 1
报表 → 车间树line_id + line_typeN : 1

按字段来源再分一次类:关联键是 report_code、machinery_id / machinery_id_type、line_id,由清洗判定填入;业务填写是表头上的人可读信息(表头名称、制品品番、修订内容、记录方式)与项目内容(项目、判定标准、方法、周期、单位),来自表内文字;系统生成是三个自增主键、check_code、del_flag、status / enable 的默认值以及审计四件套,入库时赋值;签字是两侧的 create / confirm / admit 三组,来自表内签字栏。

附录 B:术语表

术语含义
三大模块表头元数据 / 修改履历 / 点检项目
表角色某个 DDL 表在这次清洗里承担哪种数据(report / change_record / project_info)
场景一套可覆盖默认配置的参数 + 一个 report_type
健康分0~100 的单文件识别质量评分
过程记录每次清洗输出的诊断 Markdown,可外发
位置兜底列名一个都没命中时,按列位置硬套字段
契约测试用固定样例的快照锁住行为,防止规则改动悄悄改变结果