该删的和绝不能删的
一句话总结
超过保存期限的个人信息应当毫不迟延地删除,但因纠纷或监管机构要求而被施加保存令的案件,即使期限已过也不能删除。把这两条规则相冲突的地方做成可以判定的数据,就是这项工作的全部。
为什么需要它
个人信息不删除是事故,删错了也是事故。不删除,就违反了《个人信息保护法》第 21 条所说的“保存期限届满后应毫不迟延地销毁”;反过来,如果把纠纷中的索赔资料删掉,公司就失去了证明自己主张的手段。真正的事故往往是后者悄悄发生的:批处理在夜里运行,仅仅因为“期限已过”,就把调解中的案件的材料删掉了,直到下个月的调解日,都没有人知道这件事。
还有一个让判定变得困难的因素:起算日。保险的消灭时效由《商法》第 662 条规定,要点是:保险金请求权和保险费、准备金返还请求权为 3 年,保险费请求权为 2 年。但是“3 年”从什么时候开始算,数据中并不会自动写明。是事故日期,是索赔结案日,还是合同终止日?如果各个系统把不同的日期当作起算日,同一笔案件在某个系统里是销毁对象,在另一个系统里却仍是保存对象。
工作原理
设计从把四件事作为数据开始。
- 保存期限表。为每种对象写出
기산일 이름、연수和근거。把依据放在同一行,是因为日后一定会有人问“为什么是 5 年”。如果把源自法定时效的值和源自公司内部规定的值混在一起,规定变化时就不知道该改什么。 - 起算日。合同的候选是合同日期和终止日期,索赔的候选是事故日期和结案日期。尚未结束的案件没有起算日——时效还没有开始,如果把这种案件当作“日期为空,所以是很久以前”处理,进行中的索赔资料就会被整个删除。
- 保存令(legal hold)。对于纠纷、侦查、监管机构要求这类无论期限如何都必须保存的事由,以案件为单位施加。销毁判定始终按“期限是否已过”,然后“是否被施加了保存令”的顺序进行。
- 销毁凭证。记录删除了什么、什么时候、按哪条规则删除的。已经删除的东西无法再展示,所以“已经删除”这件事本身就成为唯一的证据。
日期计算的陷阱比想象的多。Python 的 datetime 中没有求“几年之后”的运算。timedelta 的单位是天和秒,所以用 days=365*3 来计算,在跨越闰年的区间里会逐日偏移。因此以年为单位的加减要用 date.replace(year=...),像 2 月 29 日这种在那一年不存在的日子,则要明确规则来处理。用 SQLite 计算时,日期和时间函数的 date(x, '+3 years') 也会把不存在的日期顺延到下个月来规范化,所以必须把处理方向写进文档,两个系统的数字才能对上。
기간 판정 ──▶ 보존 명령 제외 ──▶ 예행(dry-run) ──▶ 실제 파기 ──▶ 증적 ──▶ 남은 사본
在现场相遇的样子
最常见的事故是移动了起算日。旧规定是“自合同日期起 5 年”,新规定是“自合同终止日期起 5 年”,而批处理仍在使用旧的计算公式。如果是 10 年期的合同,两个值相差 10 年,仍然需要保存的资料会被删除几千条。这类事故只有在删除之后才会暴露。
第二种是把演练和实际执行用同一份代码来运行。用一个标志位区分的话,总有一天这个标志会被设错。如果把计划输出为文件,执行时读取该文件并重新判定后才删除,人就有了中途确认的机会。
第三种是以为已经销毁了。表里的行删了,附件文件却原样留着;运营数据库里删了,昨天拿到的备份里还原样存在。立即清除备份通常是不可能的,所以至少要用清单把哪里还留着什么暴露出来,并单独管理这份清单的处理日程。没有清单,“已删除”的报告就不是事实。
实际工作中真正重要的事
- 规则作为数据。把起算日名称、年限、依据放在表中,就可以重新计算。藏在代码里,就没有人能验证。
- 固定判定顺序。期限 → 保存令 → 演练 → 执行。顺序一换,不该删的东西就被删了。
- 未结束的案件没有起算日。不要用 0 或者很久以前的日期去填充空日期。
- 留下删除的事实。没有凭证,连已经销毁都无法证明。
- 清点剩余的副本。备份、导出文件、分析用副本都要列入清单。
下一项实验要做什么
你将创建一份包含 120 份合同、240 笔索赔、保存期限表和保存令的快照,亲自统计更改起算日后销毁对象会增加多少笔。挑出期限已过的案件,剔除被施加保存令的案件,把演练计划输出为文件,再读取这份计划进行真实删除。最后输出销毁凭证,确认在剩余文件中被删除者的联系方式是否真的读不出来,并用清单把备份中残留的副本暴露出来。