07|HarmonyOS离线盘点归档与数据保留策略【共创稿事节】
W2仓A区的秋季盘点临近收尾,盘点员已经把A-03-07货位中BRG-001轴承套件、LOT-09批号的差异复核完毕。前六篇陆续补齐了盘点行主键、重复扫码事务、批次快照、离线操作日志、版本冲突人工裁决以及复合索引。这些机制保证盘点期间的数据有序写入和查询,却留下了另一个容易拖延的问题:一批数据在业务上结束后,什么时候从可修改状态变成只读记录?什么时候可以清理临时扫描回执,哪些证据必须继续保留?
如果把“盘点完成”简单等同于删除历史数据,盘点行可能已经同步,出站操作仍在等待确认;冲突裁决完成了,裁决依据却随清理消失。相反,永远保留所有暂存记录,查询索引和本地存储又会随着批次增加而臃肿。我的处理办法是将封存、归档、保留期清理拆成三个业务动作,并为每一步设置可以解释的前置条件。

封存不是删除,先划清数据身份
盘点进行中的count_line属于工作数据,数量可随扫描和人工裁决改变。封存后生成的count_snapshot是针对一个确定修订号的只读数据集;count_operation记录离线出站操作的身份和处理回执;count_conflict保存冲突双方版本及裁决结果。四类记录承担不同责任,不应放在一张表里靠一个deleted字段决定去留。
沿用之前的复合身份countId + binId + skuId + lotNo,让快照行始终可以关联原始盘点行。封存记录还应有sealedRevision、sealedAt、manifestHash和retentionPolicyId。其中修订号对应第03篇的只读快照,不是此刻工作列表碰巧显示的页数;策略标识说明由哪份业务规则决定保留期限,便于日后调整策略时进行追溯。
工作流建议区分ACTIVE、SEALED、ARCHIVED三态。ACTIVE仍可接收扫描;SEALED表示业务录入窗口关闭且快照固定;ARCHIVED表示关联的审计资料、确认回执和清单已经形成可查询的归档集合。任何历史记录被归档,并不意味着界面要隐藏全部详情;更合适的交互是列表默认折叠旧批次,通过归档入口查阅只读明细。
第一道门槛是未完成出站操作和未裁决冲突。 第04篇的opId在网络恢复后可能被重试,必须得到服务端明确回执,不能以“最近一次请求已发出”作为完成依据。第05篇的冲突可能已被人工打开但还没选择处理结果,此时仍需阻止封存。判断只依赖应用已有的状态字段,不猜测系统何时完成网络工作。
归档资格由可审计的规则决定
下面的代码放在盘点领域层,输入是已有业务记录数组,输出待处理事项。它不关心按钮来自哪个页面,也不把网络状态等同于回执状态。
const assert = require('node:assert/strict');
function archiveBlockers(batch, operations, conflicts) {
const pending = operations.filter(op =>
op.countId === batch.countId && op.status !== 'ACKED');
const open = conflicts.filter(c =>
c.countId === batch.countId && c.status !== 'RESOLVED');
const issues = [];
if (batch.status !== 'SEALED') issues.push('批次尚未封存');
if (pending.length) issues.push(`待确认操作${pending.length}项`);
if (open.length) issues.push(`待裁决冲突${open.length}项`);
return issues;
}
const batch = { countId: 'autumn-W2', status: 'SEALED' };
assert.deepEqual(archiveBlockers(batch,
[{ countId: 'autumn-W2', status: 'PENDING' }], []),
['待确认操作1项']);
assert.deepEqual(archiveBlockers(batch,
[{ countId: 'autumn-W2', status: 'ACKED' }], []), []);
资格检查需要在提交封存的事务边界再做一次,而不能只在页面加载时计算。用户打开归档窗口后,另一个页面可能又写入了一条更晚的扫描回执;此时预览中的“可归档”已经过期。以countId加当前批次修订号作为条件,提交成功后再显示归档清单,才能让按钮状态与真实业务记录保持一致。若批次已归档,重复提交同一归档命令应返回已有归档清单,而非生成第二份历史记录。

归档清单必须能够检出遗漏
仅把批次状态改为ARCHIVED,无法回答“当时究竟保存了哪些行”。我会在只读快照的基础上另生成清单:盘点行总数、各状态数量、基准修订号、冲突裁决条数、已确认操作条数、创建时间,以及用于识别内容变化的摘要。摘要应使用稳定字段顺序、稳定行顺序和明确的字符编码。若输入顺序变化,内容相同的归档不能生成不同摘要。
下面用Node.js的crypto对规范化记录计算SHA-256。摘要适合发现传输或存储中的内容变化;身份鉴别仍应由访问控制及可信签名机制承担。这里不把普通哈希当作保密手段,也不会在展示界面泄露操作员个人信息。
const assert = require('node:assert/strict');
const { createHash } = require('node:crypto');
function manifestDigest(lines) {
const normalized = lines.map(({lineId, skuId, lotNo, quantity}) =>
({lineId, skuId, lotNo, quantity})).sort((a,b) =>
a.lineId.localeCompare(b.lineId));
return createHash('sha256').update(JSON.stringify(normalized),'utf8')
.digest('hex');
}
const rows = [
{lineId:'B',skuId:'BRG-001',lotNo:'LOT-09',quantity:19},
{lineId:'A',skuId:'BRG-002',lotNo:'LOT-03',quantity:7}
];
assert.equal(manifestDigest(rows), manifestDigest([...rows].reverse()));
assert.notEqual(manifestDigest(rows),
manifestDigest([{...rows[0],quantity:20},rows[1]]));
归档清单最好和状态变更保存在同一个本地事务中,避免状态显示为已归档却找不到清单。对于涉及备份和恢复的实现,华为开发者文档说明关系数据库具备相应的数据可靠性机制;在工程上仍须区分“备份一份数据库”和“按业务批次归档”的含义,前者不能替代逐批次的完整性清单。
保留期限应当可配置,还要有暂停清理的理由
归档后的数据不是越早删除越好。盘点结果可能涉及库存对账、差异复核、退货追溯和内部审计,因此应由业务负责人确定不同资料的保留期限。盘点结果、操作回执、冲突裁决和临时查询游标可以采用不同策略;这些期限属于应用业务配置,不应伪装成平台强制统一的天数。
我倾向于将策略拆成retainDays、legalHold、archiveVerified和purgeAfter。只有确实归档、清单完成校验、没有保留暂停标记、到达可清理时间的记录,才进入候选集合。策略调整时先产生清理预览,再由具备权限的操作员确认。清理对象应明确到具体临时表或缓存字段,不能把整个批次的审计依据一并清空。
const assert = require('node:assert/strict');
function canPurge(record, nowMs) {
return record.status === 'ARCHIVED' &&
record.archiveVerified === true &&
record.legalHold !== true &&
Number.isFinite(record.purgeAfter) &&
nowMs >= record.purgeAfter;
}
const threshold = Date.UTC(2026, 9, 26);
assert.equal(canPurge({status:'ARCHIVED',archiveVerified:true,
legalHold:false,purgeAfter:threshold-1},threshold),true);
assert.equal(canPurge({status:'ARCHIVED',archiveVerified:true,
legalHold:true,purgeAfter:threshold-1},threshold),false);
归档列表的清理入口还需要防止两个常见误区:其一,后台发起清理后立即把按钮改成“已删除”,实际存储事务可能因空间不足或权限错误失败;其二,清理时只删主表,没有清理依赖的临时索引和反向引用。更稳妥的写法是先列出依赖图,再执行小批量事务,每次确认成功才更新清理游标;出现错误时保留尚未处理的记录,并把失败阶段写入归档审计记录。

本地数据层与页面怎样衔接
HarmonyOS关系型数据库适合保存盘点批次、盘点行、操作回执、冲突裁决等有明确关联的结构化数据;Preferences更适合保存上次打开的归档筛选条件、列表视图偏好等少量配置。把大量盘点明细堆进首选项文件会让初始化读取变重,也不便于做关联查询。数据存储方案应围绕访问模式决定,而非仅因写入接口简单就采用某种容器。
页面的“封存当前批次”按钮只向应用层发出命令,不在组件中逐表删数据。仓储层负责检查版本、写入清单、更新归档状态并释放结果集;页面根据返回的原因列表决定显示错误卡片还是新的归档入口。归档过程中还要阻止同一批次新的扫描录入,而不是等存储完成后才发现已有后来写入的工作行。若出现意外退出,重进页面后依据事务记录与清单完整性恢复流程,不能仅凭上一次按钮的本地可见状态判断。
实际排查时,我会先核对countId是否一致,再确认出站操作的回执状态和冲突裁决是否全部闭合,随后比较只读快照数量与归档清单数量。如果页面显示可归档而后端仍拒绝,重点检查是否存在版本较新的操作或延迟更新的状态字段;如果清单哈希变化,先确认记录排序和字段规范是否改变,再查内容本身。最后针对清理候选逐项列出“可清理原因”,避免一次性执行缺乏解释的整库删除。
这一章让离线盘点的数据从“能写入、能同步、能检索”进一步走到“能有序退出工作态”。只要封存和清理分开,且每个转换都有数据凭据,历史盘点既能接受复核,也不会让临时记录无限积压。下一篇的异常恢复与一致性回归,将直接复用这里的归档清单、修订号和出站回执作为检查依据。
相关文档:华为开发者《数据存储方案如何选择》:https://developer.huawei.com/consumer/cn/doc/doccenter-dev-faq/faqs-local-database-management-38%EF%BC%9B%E5%8D%8E%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85%E3%80%8AData Reliability and Security Overview》:https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V14/data-reliability-security-overview-V14%E3%80%82
更多推荐

所有评论(0)