· compliance· 约 21 分钟精读

Slingguard Inspection Type Interval Due Date Boundary — Technical Analysis | KeyRotate Technology

核心要点速览 · TL;DR 概要

标准把检验分成使用前检查、定期检查与彻底检验三层,SlingGuard 把它们压进一个三成员枚举,到期日却只由一个设备周期字段推算。本文按源码核对类型、周期、记录到期日与状态判定四条线,并把 30 天预警窗口在四个档位上的占比摆在同一张表里。

Slingguard Inspection Type Interval Due Date Boundary — Technical Analysis | KeyRotate Technology

港口堆场的吊装库房里,班组每天开工前要做一次使用前检查,每六个月要把吊具交给合资格人员做一次彻底检验。两件事都写进同一本台账。于是常出现这样的画面:一条三吨扁平吊带的记录刚录完,PDF 证据页上写着「Scheduled next date: 2027-04-30」,而资产详情页顶部写着「Next Due: 2027-01-21」。两个日期都由这次操作产生,都不是伪造,但它们回答的不是同一个问题。

本文核对 SlingGuard 里与「多久算到期」有关的四条线:检验类型的枚举、设备级的周期字段、记录级的到期日字段,以及状态机的判定入口。结论按源码逐行核对。涉及的主要文件是 InspectionRecord.swift(258 行)、RiggingAsset.swift(206 行)、RiggingStore.swift(215 行)、InspectionEntryView.swift(242 行)、BatchInspectionView.swift(225 行)、ReportExporter.swift(294 行)与 CSVExporter.swift(58 行)。

一、标准把检验分成三层,应用把前两层压进一个枚举

LOLER 1998 第 9 条第(3)款把在役检验分成两类动作。第(a)项要求彻底检验:升降人员的设备与吊装附件至少每 6 个月一次,其他起重设备至少每 12 个月一次,或者按检验方案执行;第(b)项要求在两次彻底检验之间,由合资格人员按适当间隔进行检查。HSE 的说明把它拆得更细:使用者做的使用前检查、按周或按月按季做的定期检查、以及每年一到两次的彻底检验。三者的产物不同,只有彻底检验产生一份带「下次彻底检验到期日」的书面报告。

北美文本的分层类似而措辞不同。OSHA 29 CFR 1910.184(d) 要求每班使用前由指定合资格人员检查吊索和所有连接件;ASME B30.9 把检验分成初始检验、频繁检验与定期检验三级,只有定期检验要求书面记录,周期按使用工况分为每年(正常)、每月至每季(严酷),以及由合资格人员确定(特殊)。

标准中的层级典型间隔是否要求书面记录SlingGuard 枚举成员
使用前检查每班或每日否frequentPreUse
定期彻底检验(吊装附件)6 个月是thoroughExamination6Mo
定期彻底检验(其他设备)12 个月是annual12Mo
按检验方案执行方案自定是无对应成员

表格最后一行值得注意。LOLER 第 9 条第(3)款第(a)项第(iii)目允许按检验方案执行,SlingGuard 没有对应类型,只在记录里留了一个自由文本字段,标签是「Procedure / scheme reference」。这个字段会写进证据、会进 PDF、也会进 CSV,但它不参与任何日期计算。方案说九个月,应用仍按设备周期字段算。

二、三个成员与两个默认值

InspectionType 只有三个成员,rawValue 是长英文字符串,例如 6-Month Thorough Examination (LOLER/OSHA);displayName 是界面上显示的三档短名。把显示名与存储值分开是对的:前者可以随界面语言调整,后者是落盘格式,一旦发布就应冻结。三个成员的展示名分别是「Six-month examination」「Annual examination」「Pre-use check」。

两个录入界面的默认值都是六个月彻底检验。单件录入的状态初值是 .thoroughExamination6Mo,批量录入也是同一个初值。这意味着在一个全部是六个月的库房里,默认值恰好正确;在一个以十二个月设备为主的库房里,每录一条都要手动改一次类型。批量录入时这个成本被放大:一次选中二十件,二十条记录共用同一个类型初值,改一次即可,但如果库房是混合周期,批量界面就会把同一个类型写给周期不同的资产。

三、一次检验写两个字段,但只有一个字段参与算术

一次检验,写入两条时钟六个月彻底检验十二个月定期检验使用前检查记录写入的两个字段nextDueDate = 记录日期 + 设备周期lastInspectionDate 仅非预使用写回资产级 nextInspectionDue2027-01-21状态判定只读这一个记录级 nextDueDate2027-04-30只出现在 PDF 与 CSV检验类型不参与任何日期运算它只决定显示文本,以及一个布尔位:是否写回 lastInspectionDate同一台 SL-101:资产说 1 月到期,使用前检查记录说 4 月到期

单件录入的到期日由一段只有两行的计算属性给出:

private var qualifyingNextDueDate: Date {
    Calendar.current.date(byAdding: .month, value: asset.inspectionIntervalMonths, to: date) ?? date
}

属性名里带 qualifying,读起来像是「合格检验才算出到期日」。实际的计算里没有任何限定:asset.inspectionIntervalMonths 是设备字段,date 是表单上的记录日期,除这两个输入之外没有第三个。写成式子就是

dnext=addMonths(drec, masset)d_{\mathrm{next}} = \mathrm{addMonths}\left(d_{\mathrm{rec}},\, m_{\mathrm{asset}}\right)

其中周期 massetm_{\mathrm{asset}} 取自资产,记录日期 drecd_{\mathrm{rec}} 取自表单,检验类型不出现在式子里。批量录入的投影函数写法相同,只是对每件被选中的资产各算一次。InspectionRecord 的构造器在 nextDueDate 缺省时回落到记录日期加 6 个月。三处都不读 inspectionType。

把三种类型与四个周期档位交叉,得到十二种组合,记录里写下的日期只有四种可能:

选择的检验类型周期 1 个月周期 3 个月周期 6 个月周期 12 个月
六个月彻底检验记录日期加 1 月加 3 月加 6 月加 12 月
十二个月定期检验记录日期加 1 月加 3 月加 6 月加 12 月
使用前检查记录日期加 1 月加 3 月加 6 月加 12 月

同列三行完全一样,这正说明类型不参与算术。最容易读出来的矛盾是:给一台 6 个月周期的吊带选「十二个月定期检验」,记录里写下的下次到期是 6 个月后;给一台 12 个月周期的主吊环选「六个月彻底检验」,写下的是 12 个月后。操作者刚在同一个表单里选过类型,日期却不认它。

四、唯一被读取的一位

addInspectionRecord 里有一行条件:只有当记录的检验类型不是使用前检查时,才把记录日期写回资产的 lastInspectionDate。这是类型在整个判定链上唯一的用处。这个取舍本身符合标准——使用前检查不是彻底检验,不应重置六个月的法定周期。问题在于它只做了一半:记录这一侧仍然写了一个 nextDueDate,并且这个日期会被 PDF 与 CSV 当作「计划下次到期」输出。

以真机里的 SL-101 为例。它的周期档位是 6 个月,状态行写着下次到期 2027-01-21,按周期反推,最近一次彻底检验在 2026-07-21:

量值来源
资产级最近彻底检验2026-07-21上一次彻底检验记录
资产级下次到期2027-01-21最近彻底检验加 6 个月
当前状态(2026-10-30)In Service,距到期 83 天当前时间早于下次到期
2026-10-30 的使用前检查记录到期 2027-04-30记录日期加 6 个月

两个日期相差 99 天。前者决定状态,后者只被打印。同一次操作在同一份 PDF 里留下两个互不相干的日期,读者没有任何线索知道该信哪一个。更麻烦的是,这两个日期都写在证据页上,而证据页的标题是「Inspection Record Evidence」——它看起来像在陈述一个事实,实际混了一个推算值。

五、状态机只认资产级时钟

状态判定有六态,优先级固定:报废、隔离、未检验、逾期、即将到期、在用。判定分两步走。第一步读记录里的结论位:已报废或存在报废结论的记录进入报废,手动隔离或存在未被完整放行记录抵消的隔离记录进入隔离。第二步才是日期判定,而这一步只读资产级字段:lastInspectionDate 为空进入未检验;当前时间晚于下次到期进入逾期;到期日落在未来 30 天内进入即将到期;其余为在用。

nextInspectionDue 的基期是「最近彻底检验日期,没有则用投用日期」。这个兜底值带来一处口径落差:一台从未检验过的新设备,状态是未检验(第 3 条先返回),但 PDF 汇总表的「Scheduled due」列仍会打印「投用日期加周期」的日期。这个日期是推算值,不是法定到期日,报告没有标注它的性质。读到这一行的人很容易把它当成「这台设备的下次检验日」,而它其实只是「如果它从未被检验,按投用日推算会在哪天到期」。

还有一处命名与取值不一致。PDF 汇总表的列头是「Last record」,取值来自 asset.lastInspectionDate,而它排除使用前检查。明细页则逐条列出所有记录,包括使用前检查。于是一台昨天刚做过使用前检查的设备,汇总表的「最后记录」可能停在五个月前,而明细页第一行是昨天。两处都叫「记录」,指的不是同一批记录。

顺带说明,RiggingStore 里有两个为此写好的辅助方法:latestRecord(for:) 取任意最近记录,latestQualifyingExamination(for:) 排除使用前检查。全仓库没有任何调用点。判定链走的是资产字段,这两个方法没有被接进去。它们是这段设计意图留下的痕迹:有人想过把「最近记录」与「最近一次合格彻底检验」分开表达,最后没有接上。同一份代码里,两个语义不同的概念只剩一个字段承载。

六、30 天预警窗口与四个周期档位

同一段 30 天,在四个周期档位上占多少1 个月3 个月6 个月12 个月98.7%32.9%16.4%8.2%琥珀段=30 天预警窗口,条形长度正比于周期天数1 个月档位下,刚检完的资产已经落在「即将到期」区间

dueSoon 的窗口是 30 天,写死在状态函数里,与周期无关。新建资产时可以选 1、3、6、12 个月四档,于是同一个 30 天在不同档位上代表的含义差别很大:

周期档位平均周期天数30 天窗口占比每周期处于「在用」的天数
1 个月30.498.7%不足 1 天
3 个月91.232.9%约 61 天
6 个月182.416.4%约 152 天
12 个月364.88.2%约 335 天

占比的定义很简单,把 30 天窗口除以周期长度即可:

ρm=30Lm,Lm=30.4 m\rho_m = \frac{30}{L_m}, \qquad L_m = 30.4\,m

其中 mm 是周期档位(月),LmL_m 取每月 30.4 天的名义长度。代进四档得到 98.7%、32.9%、16.4% 与 8.2%。

第一行的后果是实际的。在港口与海上工况常用的 1 个月档位下,一个周期里只有不到一天不是「即将到期」:31 天的月份里,检验当天是在用,第二天起就进入预警区间,直到到期。仪表盘的六态分布因此失去分辨力——从「刚检完」到「还有三天到期」之间没有任何状态差异。

再往上一层,汇总里的合规率用「在用加即将到期」除以「总数减报废」计算。这个口径可以叫「未逾期率」,本身没有问题;但在 1 个月档位下它恒等于 100%,除非真的逾期。数字不会报错,只是不再提供信息。

七、日历月加法:181 到 184 天

到期日用的是日历月加法,不是天数加法。这符合法条的表述——「每 6 个月」指日历月,而不是 182.5 天。代价是同一个「6 个月」在不同起点上长度不同:

  • 6 个月跨度在 181 到 184 天之间,2020 至 2030 年的均值为 182.61 天;
  • 12 个月跨度是 365 或 366 天;
  • 月杪会被截断:2026-08-31 加 6 个月得到 2027-02-28,跨度 181 天;从 2027-02-28 再加 6 个月得到 2027-08-28。若每次都按到期日续期,锚点会逐年前移,三年后比原始日期早三天。

这不是缺陷,而是取舍:日历月加法保证每次间隔不超过 6 个日历月,换成固定天数反而会在某些年份越过上限。真正需要补的是说明——报告里的日期按日历月推进,遇到月杪取当月最后一天。当前 PDF 只打印一个日期,没有任何关于推进规则的说明。

八、录入面的三处值域缺口

第一,检验日期没有上下界。单件与批量两处日期选择器都只声明了显示日期,没有范围约束。操作者可以选一个未来的日期。未来日期会经 addInspectionRecord 写进 lastInspectionDate,把下次到期整体后移——状态等于被人工续期。应用不会提示这条记录来自未来。

第二,补录会把到期日往回拉。addInspectionRecord 无条件用记录日期覆盖 lastInspectionDate,没有取最大值。补录一条去年的纸质记录,到期日随之下移,一台在用设备可能立刻变成逾期。这个行为对「把历史记录补齐」是有用的,对「不小心选了去年」是有害的,两者在界面上没有区分。

第三,周期字段创建后不可改。RiggingStore 有 updateAsset,但全仓库没有调用点,界面也没有编辑入口。选错档位的唯一补救是删除资产重建,而 deleteAssets 会连带删除该资产的全部记录与放行记录。CSV 导入侧同样只有新增:遇到重复的 Tag ID 直接跳过,不会更新已有资产的周期。导入解析写的是「整数转换失败则回落 6」,缺失或非法回落 6 个月,0 与负数照单全收,没有值域校验。一个手工改过的 CSV 里写下 0,到期日等于记录日期,资产立刻逾期;写下负数,到期日在过去。

九、四条改动

第一,让类型与周期对齐,或者明确声明类型不参与算术。两种都可以,现状是最差的第三种:界面让人以为选了类型,实际只改了标签。最小改动是在选中「十二个月定期检验」时按 12 个月计算,或在类型与设备周期不一致时提示并要求确认。

第二,使用前检查不写记录级到期日。把字段改成可选,或在写入时置空;PDF 明细对这类记录打印「不适用」,CSV 输出空列。这样两条时钟就只剩一条,预使用记录只保留「谁在什么时候看过」这一件事。

第三,报告口径对齐。「Last record」改成「Last thorough examination」,或改成真正的最近记录日期;「Scheduled due」对未检验资产标注为「按投用日期推算」。这两处都只是标签与取值的一致性,不涉及判定逻辑。

第四,预警窗口按周期比例取值。把固定的 30 天改为周期的两成与 30 天取小:1 个月档位取 6 天,12 个月档位仍取 30 天。这样「即将到期」在任何档位都表示周期的最后两成,而不是在短周期上覆盖整个周期。

十、能力边界

  • 应用不判断标准适用性。录入页的脚注写明它不选择也不校验适用的检验标准。类型与周期都是操作者填的,应用只负责记录与算术。
  • 到期判定只用本机记录。没有外部证书链,没有联网核验,也没有第三方审核轨迹。
  • 记录确认是本地开关加署名文本,不是密码学签名,也不绑定证书有效期。
  • 记录日期可回溯填写,应用不做单调性校验。
  • 删除资产会删除其全部记录与放行记录,历史不保留。
  • 日历运算依赖系统时区与日历设置,跨时区补录可能得到不同的日期边界。

十一、结语

三层检验、两条时钟、一个 30 天常数。这三件事单独看都合理,放在一起就产生了本文列出的那些错位:类型是标签,周期是设备属性,到期日是记录字段,而判定只读其中一个。把标签与算术对齐,比再加一个检验类型更有用。

对一个在现场使用的合规工具来说,最要紧的不是算得多复杂,而是同一个问题只有一个答案。当一条记录同时携带两个到期日、而界面对两者的称呼又不够区分时,使用者只能靠记忆判断哪个算数。这跟记性无关,跟字段有关。

参考资料

Share:
Back to Blog