feat:人脸机器实时增量更新

This commit is contained in:
dzc committed 2026-08-17 13:39:24 +08:00
1 parent 3770173604
commit 2b9b07cf96
1 file changed
+93
@@ -0,0 +1,93 @@
/// 人脸同步的业务范围,所有入口最终映射为部门快照或用户增量。
enum FaceSyncMode {
/// 本部门及子部门的完整权威快照,用于每日对账和人工同步。
departmentSnapshot,
/// 指定用户的最新状态回查,用于 MQ 低延迟增量同步。
userDelta,
/// 非生产调试使用的全租户快照,生产界面不会暴露该模式。
debugAllSnapshot,
}
/// 一次人脸差异同步的结构化汇总,可直接映射到现有任务日志统计列。
class FaceSyncSummary {
/// 创建同步汇总;失败样本只保存脱敏标识,不保存姓名、图片地址或特征。
const FaceSyncSummary({
required this.mode,
required this.scanned,
required this.added,
required this.updated,
required this.deleted,
required this.skipped,
required this.failed,
required this.maskedFailedSampleIds,
});
/// 本次同步模式。
final FaceSyncMode mode;
/// 从服务端完整读取并校验的记录数。
final int scanned;
/// 新增并成功写入本地数据库及虹软内存库的样本数。
final int added;
/// 元数据或图片版本成功更新的样本数。
final int updated;
/// 成功从虹软、本地数据库和正式文件范围移除的孤儿样本数。
final int deleted;
/// 服务端与本地版本完全一致、未发生下载或虹软写入的样本数。
final int skipped;
/// 本次处理失败且保留待下次重试的样本数。
final int failed;
/// 失败样本的脱敏编号,最多由同步服务保留有限数量用于现场定位。
final List<String> maskedFailedSampleIds;
/// 本次成功新增、更新和删除的变更总数。
int get changed => added + updated + deleted;
/// 是否存在需要下次重试的失败样本。
bool get hasFailures => failed > 0;
/// 面向任务日志和设置页的简短结果描述。
String get message {
if (!hasFailures) {
return '扫描 $scanned,新增 $added,更新 $updated,删除 $deleted,跳过 $skipped';
}
final failedSamples = maskedFailedSampleIds.isEmpty
? ''
: ',失败样本 ${maskedFailedSampleIds.join(',')}';
return '扫描 $scanned,新增 $added,更新 $updated,删除 $deleted,失败 $failed$failedSamples';
}
}
/// V3 快照不完整、游标倒退或样本标识冲突时抛出的整次同步异常。
class FaceSyncSnapshotException implements Exception {
/// 使用不包含图片地址和人员信息的安全摘要创建快照异常。
const FaceSyncSnapshotException(this.message);
/// 可写入终端任务日志的安全错误摘要。
final String message;
/// 返回安全错误摘要。
@override
String toString() => message;
}
/// 部分样本失败时交给统一调度器记录统计的异常。
class FaceSyncPartialFailureException implements Exception {
/// 创建包含脱敏失败样本和完整计数的部分失败异常。
const FaceSyncPartialFailureException(this.summary);
/// 已完成的同步统计,失败项均保留旧数据等待下次重试。
final FaceSyncSummary summary;
/// 返回面向设置页的汇总描述。
@override
String toString() => summary.message;
}