Просмотр исходного кода

feat(s5): add IQC inspection item persistence foundation

YY968XX 4 дней назад
Родитель
Сommit
85b3c8ea85

+ 9 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET 通用权限开发平台</Description>
-    <AssemblyVersion>1.0.528</AssemblyVersion>
-    <FileVersion>1.0.528</FileVersion>
-    <Version>1.0.528</Version>
+    <AssemblyVersion>1.0.529</AssemblyVersion>
+    <FileVersion>1.0.529</FileVersion>
+    <Version>1.0.529</Version>
   </PropertyGroup>
 
   <ItemGroup>
@@ -805,6 +805,12 @@
     <None Update="UpdateScripts\1.0.528.verify.sql">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>
+    <None Update="UpdateScripts\1.0.529.sql">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="UpdateScripts\1.0.529.verify.sql">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
     <None Update="UpdateScripts\UAT-PLACEHOLDER-MENU-HIDE.ops.sql">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>

+ 139 - 0
server/Admin.NET.Web.Entry/UpdateScripts/1.0.529.sql

@@ -0,0 +1,139 @@
+-- ============================================================================
+-- 1.0.529 · S5 IQC Inspection Detail · Batch 1 Persistence Foundation
+--
+-- 用途:为后续 IQC item-level inspection 建立持久化基础(两张 Ai-DOP 自有运行表)。
+--       本批只建结构,不写任何数据、不接任何代码路径。
+--       ⇒ 应用本脚本后系统业务行为与应用前完全一致,只是多了两张空表。
+--
+-- 后续批次:Batch 2 生成时写入检规快照 / Batch 3 录入项目结果 / Batch 4 详情 UI。
+--
+-- ── 红线(本脚本自我约束)────────────────────────────────────────────────────
+--   · 只 CREATE TABLE IF NOT EXISTS 两张 ado_s5_* 新表
+--   · 零 ALTER / 零 DROP / 零 INSERT / 零 UPDATE / 零 DELETE
+--   · 不触碰 qms_jygf / qms_jygfzb / qms_qcp_inspbill / qms_qcp_insappnentry
+--     —— 尤其【不新增 qms_jygfzb.lrlx】:qms_* 属源/贴源体系(热回读 INSERT +
+--        MdpDbPushExecutor 回写白名单),非 Ai-DOP 可扩展 Schema
+--   · 不建 FOREIGN KEY(实测 204 张 ado_* 表 FK 数 = 0,符合仓库惯例)
+--   · 不注册 mdp_entity / 不建 mdp_stg / mdp_std / 不改 HotWatch / PushExecutor
+--
+-- ── 数据归属 ────────────────────────────────────────────────────────────────
+--   两张表 = S5 Operational Truth,仅 S5 IQC 模块运行期代码可直接事务读写。
+--   跨模块消费者(S6/S7/S8/S9/看板/KPI/统计/对外供数)一律禁止直接 SELECT/JOIN,
+--   必须走 ado_s5_iqc_* → mdp_entity → MDP staging/standard → consumer。
+--
+-- ── 租户隔离(四层,硬原则)──────────────────────────────────────────────────
+--   ① tenant_id NOT NULL
+--   ② 所有业务 UK / secondary index 首列 = tenant_id(PRIMARY KEY(id) 为物理主键例外)
+--   ③ valid-tenant CHECK:沿用仓库既有范式(全库 21 条 CHECK 中 18 条同形)
+--      tenant_id IS NOT NULL AND tenant_id NOT IN (0,1,1300000000001)
+--      1300000000001 = AdoS0TenantScope.cs:18 MainTenantId(超管未选目标租户的主租户)
+--   ④ 未来所有读写 SQL 必须显式带 tenant_id 谓词(本批无 C# 代码,仅为其备好边界)
+--
+-- ── 单一真源(measurement truth)────────────────────────────────────────────
+--   result_type='NUMERIC'      → 真源 = ado_s5_iqc_insp_bill_item_sample 行集
+--                                 item.qualified_qty / unqualified_qty 必须为 NULL
+--                                 (由 ck_s5_iqc_item_truth 在 DB 层强制)
+--   result_type='NON_NUMERIC'  → 真源 = item.qualified_qty / unqualified_qty
+--                                 该 item 不得有 sample 行
+--                                 (跨表约束,DB CHECK 无法表达;本批【不引入 trigger】,
+--                                   由 Batch 3 的 save-detail 强制 + 巡检 SQL 兜底)
+--   result_type IS NULL        → 不允许记录任何测量事实
+--
+-- ── 列宽依据(实测 qms_jygfzb,36686 行)────────────────────────────────────
+--   jyxm max 242 / jybz max 363 / jygjjyff max 538 / txfl max 255
+--   cyfa max 63  / zs   max 247 / sx,xx    max 7   / xh ∈ [1,91] 且 9 行为 NULL
+--   ★ method_tool 必须 VARCHAR(1000):源 jygjjyff 已有 1 条真实记录 538 字符,
+--     缩为 VARCHAR(500) 会截断(禁止擅自缩短)
+--   行宽按 utf8mb4 声明上限估算 ≈ 18.7KB,远低于 65535 ⇒ 无需任何字符集降级
+-- ============================================================================
+
+-- ============ ① IQC 检验单项目实例(检规快照 + 项目级结果)============
+CREATE TABLE IF NOT EXISTS ado_s5_iqc_insp_bill_item (
+    id                    BIGINT        NOT NULL AUTO_INCREMENT,
+    tenant_id             BIGINT        NOT NULL                COMMENT '租户ID(隔离域,所有读写必带)',
+    inspection_bill_id    BIGINT        NOT NULL                COMMENT '所属检验单 qms_qcp_inspbill.id(唯一关联键,不冗余单号)',
+    seq                   INT           NULL                    COMMENT '展示序号,快照自 qms_jygfzb.xh(可空可重复,不作键)',
+    source_spec_id        BIGINT        NOT NULL                COMMENT '来源检规 qms_jygf.id —— 仅 lineage 追溯,允许未来悬挂',
+    source_spec_item_id   BIGINT        NOT NULL                COMMENT '来源检规明细 qms_jygfzb.id —— 仅 lineage 追溯,允许未来悬挂',
+
+    -- ── Specification Snapshot:Batch 2 生成时一次写入,之后 IMMUTABLE ──
+    inspection_item       VARCHAR(500)  NULL                    COMMENT '快照 qms_jygfzb.jyxm 检验项目(实测 max 242)',
+    inspection_standard   VARCHAR(1000) NULL                    COMMENT '快照 qms_jygfzb.jybz 检验标准(实测 max 363;公差常内嵌于此)',
+    method_tool           VARCHAR(1000) NULL                    COMMENT '快照 qms_jygfzb.jygjjyff 检验工具/方法(实测 max 538 ⇒ 必须 1000,禁缩为 500)',
+    image_category        VARCHAR(500)  NULL                    COMMENT '快照 qms_jygfzb.txfl 图像分类(实测 max 255)',
+    sampling_scheme       VARCHAR(500)  NULL                    COMMENT '快照 qms_jygfzb.cyfa 抽样方案(实测 max 63)',
+    spec_remark           VARCHAR(500)  NULL                    COMMENT '快照 qms_jygfzb.zs 检规注释(实测 max 247)—— 区别于 result_remark',
+    upper_limit           VARCHAR(50)   NULL                    COMMENT '快照 qms_jygfzb.sx 上限(原样文本,不解析)',
+    lower_limit           VARCHAR(50)   NULL                    COMMENT '快照 qms_jygfzb.xx 下限(原样文本,不解析)',
+
+    -- ── Result Contract:运行期事实,仅 N1 态可写(Batch 3) ──
+    result_type           VARCHAR(20)   NULL                    COMMENT '录入类型 NUMERIC/NON_NUMERIC。★非快照★ 不来自检规;N1 首次录入时选定,N1 态可改但须清空本项目测量事实。NULL=未定(合法初态)',
+    qualified_qty         DECIMAL(18,6) NULL                    COMMENT '【NON_NUMERIC 唯一真源】合格数;NUMERIC 时必须为 NULL',
+    unqualified_qty       DECIMAL(18,6) NULL                    COMMENT '【NON_NUMERIC 唯一真源】不合格数;NUMERIC 时必须为 NULL',
+    item_judgement        VARCHAR(16)   NULL                    COMMENT '项目判定 PASS/FAIL/INVALID;NULL=未录。派生值,后端唯一写入方(已删除 PENDING 枚举)',
+    judgement_source      VARCHAR(8)    NULL                    COMMENT 'AUTO=由真源实时重算 / MANUAL=检验员显式结论;测量事实一旦变更即强制复位为 AUTO',
+    result_remark         VARCHAR(500)  NULL                    COMMENT '项目级结果备注(≠ spec_remark)',
+    last_result_by        BIGINT        NULL                    COMMENT '最后录入人 SysUser.Id',
+    last_result_at        DATETIME      NULL                    COMMENT '最后录入时间',
+
+    -- ── Audit:与同类 raw-DDL 表(ado_s6_process_inspection_*)一致,不加 create_user/is_deleted ──
+    create_time           DATETIME      NULL DEFAULT CURRENT_TIMESTAMP,
+    update_time           DATETIME      NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_s5_iqc_item_scope (tenant_id, inspection_bill_id, source_spec_item_id),
+    KEY        idx_s5_iqc_item_src   (tenant_id, source_spec_id),
+
+    CONSTRAINT ck_s5_iqc_item_tenant CHECK (
+        tenant_id IS NOT NULL AND tenant_id NOT IN (0, 1, 1300000000001)
+    ),
+    CONSTRAINT ck_s5_iqc_item_result_type CHECK (
+        result_type IS NULL OR result_type IN ('NUMERIC', 'NON_NUMERIC')
+    ),
+    -- 单一真源:NUMERIC 分支不得携带计数(其真源在 sample 表)。
+    -- NULL 分支显式声明放行,不依赖 MySQL「CHECK 求值为 NULL 视为满足」的隐式行为。
+    CONSTRAINT ck_s5_iqc_item_truth CHECK (
+        result_type IS NULL
+        OR result_type <> 'NUMERIC'
+        OR (qualified_qty IS NULL AND unqualified_qty IS NULL)
+    ),
+    CONSTRAINT ck_s5_iqc_item_judgement CHECK (
+        item_judgement IS NULL OR item_judgement IN ('PASS', 'FAIL', 'INVALID')
+    ),
+    CONSTRAINT ck_s5_iqc_item_jsrc CHECK (
+        judgement_source IS NULL OR judgement_source IN ('AUTO', 'MANUAL')
+    )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
+  COMMENT='S5 IQC检验单项目实例(检规快照+项目级结果)。S5 Operational Truth;跨模块消费须经 MDP,禁止直接 JOIN';
+
+-- ============ ② IQC 检验单项目实测值(逐样本,仅 NUMERIC 使用)============
+CREATE TABLE IF NOT EXISTS ado_s5_iqc_insp_bill_item_sample (
+    id                    BIGINT        NOT NULL AUTO_INCREMENT,
+    tenant_id             BIGINT        NOT NULL                COMMENT '租户ID(隔离域,所有读写必带)',
+    inspection_bill_id    BIGINT        NOT NULL                COMMENT '反规范化:支持按单一次取全部样本免 JOIN',
+    item_id               BIGINT        NOT NULL                COMMENT '所属项目 ado_s5_iqc_insp_bill_item.id',
+    sample_index          INT           NOT NULL                COMMENT '样本序号 1..N(无硬上限,不用 j1..j50 定长列)',
+    sample_value_raw      VARCHAR(200)  NOT NULL                COMMENT '【NUMERIC 唯一真源】检验员录入原文,原样保留(供 INVALID 复盘)。空值语义=该样本不存在⇒删行,不存空串',
+    sample_numeric        DECIMAL(18,6) NULL                    COMMENT '后端解析值;不可解析=NULL(不丢原文)',
+    judgement             VARCHAR(16)   NULL                    COMMENT 'PASS/FAIL/INVALID;后端唯一写入方,前端传值一律忽略',
+    remark                VARCHAR(500)  NULL                    COMMENT '样本级备注',
+    inspector_id          BIGINT        NULL                    COMMENT '样本级录入人 SysUser.Id',
+    inspection_time       DATETIME      NULL                    COMMENT '样本级录入时刻',
+    create_time           DATETIME      NULL DEFAULT CURRENT_TIMESTAMP,
+    update_time           DATETIME      NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_s5_iqc_sample_scope (tenant_id, item_id, sample_index),
+    KEY        idx_s5_iqc_sample_bill (tenant_id, inspection_bill_id),
+
+    CONSTRAINT ck_s5_iqc_sample_tenant CHECK (
+        tenant_id IS NOT NULL AND tenant_id NOT IN (0, 1, 1300000000001)
+    ),
+    CONSTRAINT ck_s5_iqc_sample_index CHECK (
+        sample_index >= 1
+    ),
+    CONSTRAINT ck_s5_iqc_sample_judgement CHECK (
+        judgement IS NULL OR judgement IN ('PASS', 'FAIL', 'INVALID')
+    )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
+  COMMENT='S5 IQC检验单项目实测值(逐样本,仅NUMERIC)。S5 Operational Truth;跨模块消费须经 MDP,禁止直接 JOIN';

+ 129 - 0
server/Admin.NET.Web.Entry/UpdateScripts/1.0.529.verify.sql

@@ -0,0 +1,129 @@
+-- ============================================================================
+-- 1.0.529 校验 · S5 IQC Inspection Detail · Batch 1 Persistence Foundation
+-- 约定:每条必须返回真值,否则 AutoVersionUpdate 判定该版本失败。
+--       全部为纯 SELECT(runner 用 SqlQuerySingle 取首行首列,不支持 SET 变量)。
+--
+-- 覆盖 Batch 1 验收项 A/B/C/D/E/F:表存在 · 列逐项 · tenant-first 索引 · CHECK ·
+--                                  qms_* 未改 · 无业务数据写入
+-- ============================================================================
+
+-- ---- A1 · 两张新表都存在 ----
+SELECT COUNT(*) = 2 FROM information_schema.TABLES
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME IN ('ado_s5_iqc_insp_bill_item', 'ado_s5_iqc_insp_bill_item_sample');
+
+-- ---- A2 · engine / collation 与仓库惯例一致 ----
+SELECT COUNT(*) = 2 FROM information_schema.TABLES
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME IN ('ado_s5_iqc_insp_bill_item', 'ado_s5_iqc_insp_bill_item_sample')
+  AND ENGINE = 'InnoDB' AND TABLE_COLLATION = 'utf8mb4_0900_ai_ci';
+
+-- ---- B1 · item 表列数 = 24 ----
+SELECT COUNT(*) = 24 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item';
+
+-- ---- B2 · sample 表列数 = 13 ----
+SELECT COUNT(*) = 13 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item_sample';
+
+-- ---- B3 · method_tool 必须是 varchar(1000)(源 jygjjyff 实测 538 字符,500 会截断) ----
+SELECT COUNT(*) = 1 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item'
+  AND COLUMN_NAME = 'method_tool'
+  AND DATA_TYPE = 'varchar' AND CHARACTER_MAXIMUM_LENGTH = 1000;
+
+-- ---- B4 · 其余快照列宽逐项 ----
+SELECT COUNT(*) = 7 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item'
+  AND ((COLUMN_NAME = 'inspection_item'     AND CHARACTER_MAXIMUM_LENGTH = 500)
+    OR (COLUMN_NAME = 'inspection_standard' AND CHARACTER_MAXIMUM_LENGTH = 1000)
+    OR (COLUMN_NAME = 'image_category'      AND CHARACTER_MAXIMUM_LENGTH = 500)
+    OR (COLUMN_NAME = 'sampling_scheme'     AND CHARACTER_MAXIMUM_LENGTH = 500)
+    OR (COLUMN_NAME = 'spec_remark'         AND CHARACTER_MAXIMUM_LENGTH = 500)
+    OR (COLUMN_NAME = 'upper_limit'         AND CHARACTER_MAXIMUM_LENGTH = 50)
+    OR (COLUMN_NAME = 'lower_limit'         AND CHARACTER_MAXIMUM_LENGTH = 50));
+
+-- ---- B5 · item 表 NOT NULL 业务列恰好是这 4 个(id 由 PK 保证,不计入) ----
+SELECT COUNT(*) = 4 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item'
+  AND IS_NULLABLE = 'NO'
+  AND COLUMN_NAME IN ('tenant_id', 'inspection_bill_id', 'source_spec_id', 'source_spec_item_id');
+
+-- ---- B6 · seq 必须允许 NULL(源 qms_jygfzb.xh 实测 9 行为 NULL) ----
+SELECT COUNT(*) = 1 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item'
+  AND COLUMN_NAME = 'seq' AND IS_NULLABLE = 'YES';
+
+-- ---- B7 · sample 表 NOT NULL 列 ----
+SELECT COUNT(*) = 5 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item_sample'
+  AND IS_NULLABLE = 'NO'
+  AND COLUMN_NAME IN ('tenant_id', 'inspection_bill_id', 'item_id', 'sample_index', 'sample_value_raw');
+
+-- ---- B8 · 两个 DECIMAL 真源列精度 = (18,6),与 ado_s5_iqc_sqe_disposition 惯例一致 ----
+SELECT COUNT(*) = 3 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND ((TABLE_NAME = 'ado_s5_iqc_insp_bill_item'        AND COLUMN_NAME IN ('qualified_qty', 'unqualified_qty'))
+    OR (TABLE_NAME = 'ado_s5_iqc_insp_bill_item_sample' AND COLUMN_NAME = 'sample_numeric'))
+  AND NUMERIC_PRECISION = 18 AND NUMERIC_SCALE = 6;
+
+-- ---- C1 · tenant-first:所有非 PRIMARY 索引的首列必须是 tenant_id ----
+SELECT COUNT(*) = 0 FROM information_schema.STATISTICS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME IN ('ado_s5_iqc_insp_bill_item', 'ado_s5_iqc_insp_bill_item_sample')
+  AND INDEX_NAME <> 'PRIMARY'
+  AND SEQ_IN_INDEX = 1
+  AND COLUMN_NAME <> 'tenant_id';
+
+-- ---- C2 · item UK 恰为 (tenant_id, inspection_bill_id, source_spec_item_id) ----
+SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) = 'tenant_id,inspection_bill_id,source_spec_item_id'
+FROM information_schema.STATISTICS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item'
+  AND INDEX_NAME = 'uk_s5_iqc_item_scope' AND NON_UNIQUE = 0;
+
+-- ---- C3 · sample UK 恰为 (tenant_id, item_id, sample_index) ----
+SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) = 'tenant_id,item_id,sample_index'
+FROM information_schema.STATISTICS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s5_iqc_insp_bill_item_sample'
+  AND INDEX_NAME = 'uk_s5_iqc_sample_scope' AND NON_UNIQUE = 0;
+
+-- ---- C4 · 两张表不得存在 FOREIGN KEY(实测 204 张 ado_* 表 FK 数 = 0) ----
+SELECT COUNT(*) = 0 FROM information_schema.TABLE_CONSTRAINTS
+WHERE CONSTRAINT_SCHEMA = DATABASE()
+  AND TABLE_NAME IN ('ado_s5_iqc_insp_bill_item', 'ado_s5_iqc_insp_bill_item_sample')
+  AND CONSTRAINT_TYPE = 'FOREIGN KEY';
+
+-- ---- D1 · 8 条 CHECK 约束齐备 ----
+SELECT COUNT(*) = 8 FROM information_schema.TABLE_CONSTRAINTS
+WHERE CONSTRAINT_SCHEMA = DATABASE()
+  AND TABLE_NAME IN ('ado_s5_iqc_insp_bill_item', 'ado_s5_iqc_insp_bill_item_sample')
+  AND CONSTRAINT_TYPE = 'CHECK'
+  AND CONSTRAINT_NAME IN ('ck_s5_iqc_item_tenant', 'ck_s5_iqc_item_result_type',
+                          'ck_s5_iqc_item_truth', 'ck_s5_iqc_item_judgement',
+                          'ck_s5_iqc_item_jsrc',
+                          'ck_s5_iqc_sample_tenant', 'ck_s5_iqc_sample_index',
+                          'ck_s5_iqc_sample_judgement');
+
+-- ---- D2 · 两条 valid-tenant CHECK 确实排除 0 / 1 / 主租户哨兵 ----
+SELECT COUNT(*) = 2 FROM information_schema.CHECK_CONSTRAINTS
+WHERE CONSTRAINT_SCHEMA = DATABASE()
+  AND CONSTRAINT_NAME IN ('ck_s5_iqc_item_tenant', 'ck_s5_iqc_sample_tenant')
+  AND CHECK_CLAUSE LIKE '%1300000000001%'
+  AND CHECK_CLAUSE LIKE '%is not null%';
+
+-- ---- D3 · 单一真源 CHECK 显式覆盖 NUMERIC 分支且不依赖三值逻辑隐式放行 ----
+SELECT COUNT(*) = 1 FROM information_schema.CHECK_CONSTRAINTS
+WHERE CONSTRAINT_SCHEMA = DATABASE()
+  AND CONSTRAINT_NAME = 'ck_s5_iqc_item_truth'
+  AND CHECK_CLAUSE LIKE '%result_type%is null%'
+  AND CHECK_CLAUSE LIKE '%qualified_qty%';
+
+-- ---- E1 · qms_jygfzb 不得被新增 lrlx 列(本批对 qms_* 零 ALTER 的结构证明) ----
+SELECT COUNT(*) = 0 FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qms_jygfzb' AND COLUMN_NAME = 'lrlx';
+
+-- ---- F1 · 本批完成时 item 表必须为空(零业务数据写入) ----
+SELECT COUNT(*) = 0 FROM ado_s5_iqc_insp_bill_item;
+
+-- ---- F2 · 本批完成时 sample 表必须为空 ----
+SELECT COUNT(*) = 0 FROM ado_s5_iqc_insp_bill_item_sample;