feat:收银页面也可以快速注册人脸

This commit is contained in:
dzc committed 2026-09-10 17:05:03 +08:00
1 parent 24a7867ab4
commit e15197edf4
2 files changed
+443 -10

No files matched your search

+25 -6
View File
@@ -55,9 +55,13 @@ abstract class SyncFaceTool {
///
/// 完整快照会一次性拉入内存并完成唯一性、游标和页面连续性校验,
/// 任何页面中断都会抛出异常且不会进入本地孤儿删除阶段。
///
/// [onProgress] 在清单分页和逐条处理时上报进度;清单仍在分页获取时总数
/// 为空,进入逐条处理后总数为本次需要处理的记录数。
static Future<FaceSyncSummary> sync({
required FaceSyncMode mode,
Set<String>? userIds,
void Function(int processed, int? total)? onProgress,
}) async {
if (!AppNetworkTool.isAutoNetwork()) {
throw const FaceSyncSnapshotException('网络不可用,无法获取人脸权威快照');
@@ -108,18 +112,23 @@ abstract class SyncFaceTool {
local: local,
storagePath: storagePath,
accumulator: accumulator,
onProgress: onProgress,
);
}
} else {
final remote = mode == FaceSyncMode.debugAllSnapshot
? await _loadDebugAllSnapshot()
: await _loadV3Snapshot(merchantDeptId: merchantDeptId);
? await _loadDebugAllSnapshot(onProgress: onProgress)
: await _loadV3Snapshot(
merchantDeptId: merchantDeptId,
onProgress: onProgress,
);
final local = await Get.find<AppControl>().dataBase.getFaceDao.getAll();
await _reconcile(
remote: remote,
local: local,
storagePath: storagePath,
accumulator: accumulator,
onProgress: onProgress,
);
}
await _reportPersistedFaceQualitySafely();
@@ -129,9 +138,12 @@ abstract class SyncFaceTool {
}
/// 拉取 V3 固定上界游标快照并校验页面连续性、记录唯一性和字段有效性。
///
/// [onProgress] 在每页合并后上报当前已获取条数;分页总数未知,回调总数为空。
static Future<List<ServiceFaceSyncRecord>> _loadV3Snapshot({
required String merchantDeptId,
String? userId,
void Function(int processed, int? total)? onProgress,
}) async {
var afterId = '0';
String? snapshotMaxId;
@@ -188,6 +200,7 @@ abstract class SyncFaceTool {
throw const FaceSyncSnapshotException('人脸快照存在空页但仍声明后续数据');
}
afterId = snapshot.nextAfterId;
onProgress?.call(records.length, null);
}
throw const FaceSyncSnapshotException('人脸快照分页超过安全上限');
}
@@ -195,7 +208,9 @@ abstract class SyncFaceTool {
/// 使用保留的 V2 接口拉取非生产调试全租户快照。
///
/// 生产入口不会调用本方法;V2 不提供源更新时间,因此仅以图片地址判断图片版本。
static Future<List<ServiceFaceSyncRecord>> _loadDebugAllSnapshot() async {
static Future<List<ServiceFaceSyncRecord>> _loadDebugAllSnapshot({
void Function(int processed, int? total)? onProgress,
}) async {
final records = <ServiceFaceSyncRecord>[];
final sampleIds = <String>{};
for (var page = 1; page <= _maxSnapshotPages; page++) {
@@ -228,6 +243,7 @@ abstract class SyncFaceTool {
));
}
if (data.length < _debugPageSize) return records;
onProgress?.call(records.length, null);
}
throw const FaceSyncSnapshotException('调试全租户快照分页超过安全上限');
}
@@ -238,6 +254,7 @@ abstract class SyncFaceTool {
required List<FaceEntity> local,
required String storagePath,
required _FaceSyncAccumulator accumulator,
void Function(int processed, int? total)? onProgress,
}) async {
final faceDao = Get.find<AppControl>().dataBase.getFaceDao;
final localBySampleId = <String, FaceEntity>{};
@@ -266,23 +283,25 @@ abstract class SyncFaceTool {
} else {
accumulator.fail(record.sampleId);
}
continue;
}
if (_metadataChanged(existing, record)) {
} else if (_metadataChanged(existing, record)) {
await faceDao.edit(_toEntity(record, existing: existing));
accumulator.updated++;
} else {
accumulator.skipped++;
}
onProgress?.call(accumulator.scanned, remote.length);
}
// 只有远端完整集合已经成功拉取和校验后才会执行此处的集合差删除。
// 孤儿数量已知且通常远小于远端集合,并入总数保持进度单调推进。
final orphanTotal = remote.length + localBySampleId.length;
for (final orphan in localBySampleId.values) {
if (await _deleteLocalFace(orphan, faceDao)) {
accumulator.deleted++;
} else {
accumulator.fail(orphan.sampleId);
}
onProgress?.call(remote.length + accumulator.deleted, orphanTotal);
}
}