feat:人脸质量上报、人脸支付对比凭证
This commit is contained in:
1 parent
e15197edf4
commit
dc5ef2bc9e
9 files changed
+341
-130
No files matched your search
@@ -792,6 +792,10 @@ abstract final class OrderEvidenceUploadTool {
|
||||
!(task.androidOrderCode?.isNotEmpty ?? false);
|
||||
|
||||
/// 判断服务端拒绝是否属于哈希、格式或归属等确定不可恢复的错误。
|
||||
///
|
||||
/// 注意“订单不存在或不属于当前设备”不能列为永久失败:pj 离线人脸订单在下单时只
|
||||
/// 绑定终端订单号,服务端订单要等联网同步后才存在,该拒绝必须按退避重试,由
|
||||
/// 30 天本地保留期限兜底收敛。
|
||||
static bool _isPermanentError(Object error) {
|
||||
if (error is! DioException) return false;
|
||||
final message = '${error.error ?? ''} ${error.message ?? ''}';
|
||||
@@ -800,7 +804,6 @@ abstract final class OrderEvidenceUploadTool {
|
||||
'哈希不一致',
|
||||
'不受信任',
|
||||
'地址不合法',
|
||||
'不属于当前设备',
|
||||
'用户不一致',
|
||||
'样本不一致',
|
||||
'图片大小与终端上报不一致',
|
||||
|
||||
@@ -131,8 +131,10 @@ abstract class SyncFaceTool {
|
||||
onProgress: onProgress,
|
||||
);
|
||||
}
|
||||
await _reportPersistedFaceQualitySafely();
|
||||
final summary = accumulator.toSummary();
|
||||
final qualityReported = await _reportPersistedFaceQualitySafely();
|
||||
final summary = accumulator.toSummary(
|
||||
qualityReportNote: qualityReported ? null : '质量上报未完成,将在下次同步重试',
|
||||
);
|
||||
logger.i('|$_tag|同步完成|${summary.message}|');
|
||||
return summary;
|
||||
}
|
||||
@@ -207,12 +209,16 @@ abstract class SyncFaceTool {
|
||||
|
||||
/// 使用保留的 V2 接口拉取非生产调试全租户快照。
|
||||
///
|
||||
/// 生产入口不会调用本方法;V2 不提供源更新时间,因此仅以图片地址判断图片版本。
|
||||
/// 生产入口不会调用本方法;V2 以服务端 updateTime(缺失时回退 createTime)作为
|
||||
/// 图片版本,保证调试同步写入的人脸同样可以参与质量上报和版本比对。
|
||||
/// 服务端历史脏数据(空编号或重复编号)只跳过并记录计数,不阻断调试同步。
|
||||
static Future<List<ServiceFaceSyncRecord>> _loadDebugAllSnapshot({
|
||||
void Function(int processed, int? total)? onProgress,
|
||||
}) async {
|
||||
final records = <ServiceFaceSyncRecord>[];
|
||||
final sampleIds = <String>{};
|
||||
var invalidCount = 0;
|
||||
var duplicateCount = 0;
|
||||
for (var page = 1; page <= _maxSnapshotPages; page++) {
|
||||
final data = await UserApi.getFaceListV2({
|
||||
'current': page,
|
||||
@@ -228,9 +234,13 @@ abstract class SyncFaceTool {
|
||||
if (sampleId == null ||
|
||||
userId == null ||
|
||||
sampleId.isEmpty ||
|
||||
userId.isEmpty ||
|
||||
!sampleIds.add(sampleId)) {
|
||||
throw const FaceSyncSnapshotException('调试全租户快照包含无效或重复人脸编号');
|
||||
userId.isEmpty) {
|
||||
invalidCount++;
|
||||
continue;
|
||||
}
|
||||
if (!sampleIds.add(sampleId)) {
|
||||
duplicateCount++;
|
||||
continue;
|
||||
}
|
||||
records.add(ServiceFaceSyncRecord(
|
||||
id: face.id ?? sampleId,
|
||||
@@ -240,9 +250,17 @@ abstract class SyncFaceTool {
|
||||
deptId: face.deptId ?? '',
|
||||
deptName: face.deptName,
|
||||
faceImg: face.faceImg,
|
||||
sourceUpdatedAt: face.updateTime ?? face.createTime,
|
||||
));
|
||||
}
|
||||
if (data.length < _debugPageSize) return records;
|
||||
if (data.length < _debugPageSize) {
|
||||
if (invalidCount > 0 || duplicateCount > 0) {
|
||||
logger.w(
|
||||
'|$_tag|调试快照跳过异常编号|invalid:$invalidCount|duplicate:$duplicateCount|',
|
||||
);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
onProgress?.call(records.length, null);
|
||||
}
|
||||
throw const FaceSyncSnapshotException('调试全租户快照分页超过安全上限');
|
||||
@@ -306,6 +324,10 @@ abstract class SyncFaceTool {
|
||||
}
|
||||
|
||||
/// 判断图片版本、特征或正式文件是否变化;完全一致时不下载也不写虹软。
|
||||
///
|
||||
/// 历史本地记录可能缺少图片版本时间(早期 V2 调试同步写入的 updateTime 为空);
|
||||
/// 此时只要图片地址一致且本地特征与文件完好,就不再强制重新下载,由
|
||||
/// [_metadataChanged] 的版本回填补齐,避免服务端图片损坏时记录永远无法修复。
|
||||
static Future<bool> _imageChanged(
|
||||
FaceEntity? existing,
|
||||
ServiceFaceSyncRecord record,
|
||||
@@ -313,6 +335,7 @@ abstract class SyncFaceTool {
|
||||
if (existing == null) return true;
|
||||
if (existing.imagePath != record.faceImg) return true;
|
||||
if (record.sourceUpdatedAt != null &&
|
||||
existing.updateTime != null &&
|
||||
existing.updateTime != record.sourceUpdatedAt) {
|
||||
return true;
|
||||
}
|
||||
@@ -321,7 +344,9 @@ abstract class SyncFaceTool {
|
||||
return path == null || path.isEmpty || !await File(path).exists();
|
||||
}
|
||||
|
||||
/// 判断姓名或部门元数据是否变化;此类变化不需要重新提取图片特征。
|
||||
/// 判断姓名、部门元数据或图片版本时间是否变化;此类变化不需要重新提取图片特征。
|
||||
///
|
||||
/// 版本时间以服务端快照为准回填,用于修复缺少上报版本的本地历史记录。
|
||||
static bool _metadataChanged(
|
||||
FaceEntity existing,
|
||||
ServiceFaceSyncRecord record,
|
||||
@@ -329,7 +354,9 @@ abstract class SyncFaceTool {
|
||||
existing.userId != record.userId ||
|
||||
existing.userName != (record.username ?? '') ||
|
||||
existing.deptId != record.deptId ||
|
||||
existing.deptName != (record.deptName ?? '');
|
||||
existing.deptName != (record.deptName ?? '') ||
|
||||
(record.sourceUpdatedAt != null &&
|
||||
existing.updateTime != record.sourceUpdatedAt);
|
||||
|
||||
/// 下载临时图片、提取特征并提交文件、SQLite 和虹软单样本增量。
|
||||
///
|
||||
@@ -852,7 +879,7 @@ class _FaceSyncAccumulator {
|
||||
}
|
||||
|
||||
/// 生成可公开给任务日志和页面的不可变汇总。
|
||||
FaceSyncSummary toSummary() => FaceSyncSummary(
|
||||
FaceSyncSummary toSummary({String? qualityReportNote}) => FaceSyncSummary(
|
||||
mode: mode,
|
||||
scanned: scanned,
|
||||
added: added,
|
||||
@@ -861,5 +888,6 @@ class _FaceSyncAccumulator {
|
||||
skipped: skipped,
|
||||
failed: failed,
|
||||
maskedFailedSampleIds: List.unmodifiable(maskedFailedSampleIds),
|
||||
qualityReportNote: qualityReportNote,
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user