Files
biz_xfj_cloud/lib/common/utils/plugs/face_discern_plugs.dart
T
2026-08-31 15:09:38 +08:00

406 lines
12 KiB
Dart

import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class FaceDiscernPlugs {
static const String _tag = "人脸识别plug";
static const MethodChannel _channel = MethodChannel(
Constants.facePlugsMethodChannel,
);
static const EventChannel _eventChannel = EventChannel(
Constants.facePlugsEventChannel,
);
static const EventChannel _sessionEventChannel = EventChannel(
'local/face_discern_session_event',
);
static const EventChannel _registrationEventChannel = EventChannel(
Constants.facePlugsRegistrationEventChannel,
);
/// 原生在库图片质量批量检测进度通道。
static const EventChannel _qualityAuditEventChannel = EventChannel(
Constants.facePlugsQualityAuditEventChannel,
);
static MethodChannel get getChannel => _channel;
static Stream<FaceStream?>? _stream;
static Stream<FaceRecognitionSessionEvent>? _sessionStream;
static Stream<FaceRegistrationProgress>? _registrationStream;
/// 延迟创建并复用的在库图片质量检测进度流。
static Stream<FaceQualityAuditProgress>? _qualityAuditStream;
static Future<bool> jumpFaceDiscernPage() async {
try {
await _channel.invokeMethod('jumpFaceDiscern');
return true;
} catch (e) {
logger.w("|$_tag| jumpFaceDiscernPage 错误: $e");
}
return false;
}
static Future<FaceRecognitionPreviewTexture> createPreviewTexture() async {
final map = await _channel.invokeMethod<dynamic>('createPreviewTexture');
if (map is! Map) throw StateError('原生未返回有效的人脸预览纹理');
return FaceRecognitionPreviewTexture.fromMap(map);
}
static Future<void> releasePreviewTexture(int textureId) async {
await _channel.invokeMethod<void>('releasePreviewTexture', {
'textureId': textureId,
});
}
static Future<FaceRecognitionSessionEvent> startRecognition({
required String sessionId,
required FaceRecognitionScene scene,
}) {
return _invokeSession('startRecognition', {
'sessionId': sessionId,
'scene': scene.name,
});
}
static Future<FaceRecognitionSessionEvent> retryRecognition(
String sessionId,
) {
return _invokeSession('retryRecognition', {'sessionId': sessionId});
}
static Future<FaceRecognitionSessionEvent> cancelRecognition(
String sessionId, {
String? message,
}) {
return _invokeSession('cancelRecognition', {
'sessionId': sessionId,
'message': message,
});
}
static Future<FaceRecognitionSessionEvent> finishRecognition(
String sessionId,
) {
return _invokeSession('finishRecognition', {'sessionId': sessionId});
}
static Future<FaceRecognitionSessionEvent> suspendRecognition({
String? reason,
}) {
return _invokeSession('suspendRecognition', {'reason': reason});
}
static Future<FaceRecognitionSessionEvent> getRecognitionState() {
return _invokeSession('getRecognitionState');
}
/// 通过 Android Camera 枚举检查终端是否存在摄像头,不打开设备也不占用识别资源。
static Future<bool> hasCamera() async {
try {
return await _channel.invokeMethod<bool>('hasCamera') ?? false;
} catch (e) {
logger.w('|$_tag| hasCamera 错误: $e');
return false;
}
}
/// 清理 Android 私有目录中超过截止时间且未被 Floor 任务引用的证据图片。
///
/// [referencedPaths] 仅在进程内传给原生层做路径保护,不会写入日志或发送到服务端。
static Future<int> cleanupOrderEvidenceFiles({
required Set<String> referencedPaths,
required DateTime olderThan,
}) async {
return await _channel.invokeMethod<int>('cleanupOrderEvidenceFiles', {
'referencedPaths': referencedPaths.toList(growable: false),
'olderThan': olderThan.millisecondsSinceEpoch,
}) ??
0;
}
static Future<FaceRecognitionConfiguration> getRecognizeConfiguration() {
return _invokeRecognizeConfiguration('getRecognizeConfiguration');
}
static Future<FaceRecognitionConfiguration> setRecognizeConfiguration(
FaceRecognitionConfiguration configuration,
) {
return _invokeRecognizeConfiguration(
'setRecognizeConfiguration',
configuration.toMap(),
);
}
static Future<FaceRecognitionConfiguration> resetRecognizeConfiguration() {
return _invokeRecognizeConfiguration('resetRecognizeConfiguration');
}
static Stream<FaceRecognitionSessionEvent> get sessionStream {
_sessionStream ??= _sessionEventChannel
.receiveBroadcastStream()
.where((event) => event is Map)
.map((event) => FaceRecognitionSessionEvent.fromMap(event as Map))
.asBroadcastStream();
return _sessionStream!;
}
static Future<FaceRecognitionSessionEvent> _invokeSession(
String method, [
Map<String, dynamic>? arguments,
]) async {
final map = await _channel.invokeMethod<dynamic>(method, arguments);
if (map is! Map) throw StateError('原生未返回有效的人脸会话状态');
return FaceRecognitionSessionEvent.fromMap(map);
}
static Future<FaceRecognitionConfiguration> _invokeRecognizeConfiguration(
String method, [
Map<String, dynamic>? arguments,
]) async {
final map = await _channel.invokeMethod<dynamic>(method, arguments);
if (map is! Map) throw StateError('原生未返回有效的人脸识别配置');
return FaceRecognitionConfiguration.fromMap(map);
}
static Future<FaceConfig?> getConfig() async {
try {
final Map<Object?, Object?>? map =
await _channel.invokeMethod('getConfig');
if (map == null) return null;
final encode = json.encode(map);
return FaceConfig.fromJson(json.decode(encode));
} catch (e) {
logger.w("|$_tag| getConfig 错误: $e");
}
return null;
}
static Future<bool> active({
required String appId,
required String sdkKey,
required String activeKey,
}) async {
try {
final pa = {
"appId": appId,
"sdkKey": sdkKey,
"activeKey": activeKey,
};
final isActive = await _channel.invokeMethod('active', pa);
return isActive;
} catch (e) {
logger.w("|$_tag| getConfig 错误: $e");
}
return false;
}
/// 查询真实授权文件、引擎和内存人脸库状态。
static Future<FacePluginStatus?> getFaceStatus({
required String appId,
required String sdkKey,
required String activeKey,
}) async {
try {
final map = await _channel.invokeMethod<dynamic>('getFaceStatus', {
'appId': appId,
'sdkKey': sdkKey,
'activeKey': activeKey,
});
return map is Map ? FacePluginStatus.fromMap(map) : null;
} catch (e) {
logger.w('|$_tag| getFaceStatus 错误: $e');
return null;
}
}
static Future<FaceActivationResult> activateFaceSdk({
required String appId,
required String sdkKey,
required String activeKey,
}) async {
try {
final map = await _channel.invokeMethod<dynamic>('activateFaceSdk', {
'appId': appId,
'sdkKey': sdkKey,
'activeKey': activeKey,
});
if (map is Map) return FaceActivationResult.fromMap(map);
} catch (e) {
logger.w('|$_tag| activateFaceSdk 错误: $e');
}
return const FaceActivationResult(
success: false,
code: -1,
message: '人脸插件激活失败,请稍后重试',
requiresPermission: false,
canOpenSettings: false,
);
}
static Future<FaceRegistrationResult> rebuildFaceLibrary({
String? userId,
}) async {
final map = await _channel.invokeMethod<dynamic>('rebuildFaceLibrary', {
'userId': userId,
});
if (map is Map) return FaceRegistrationResult.fromMap(map);
return const FaceRegistrationResult(
success: false,
message: '人脸库注册失败,请稍后重试',
total: 0,
successCount: 0,
failedCount: 0,
failedSampleIds: [],
durationMs: 0,
);
}
/// 从临时图片提取注册特征,不修改虹软内存库和本地数据库。
static Future<FaceFeatureExtractionResult> extractFaceFeature({
required String filePath,
}) async {
final map = await _channel.invokeMethod<dynamic>('extractFaceFeature', {
'filePath': filePath,
});
if (map is Map) return FaceFeatureExtractionResult.fromMap(map);
return const FaceFeatureExtractionResult(
success: false,
message: '原生未返回有效的人脸特征结果',
featureData: [],
);
}
/// 在原生人脸操作互斥边界内批量检测本地在库照片质量。
///
/// 文件路径仅传入本机 Android 插件;原生逐图解码并通过 [qualityAuditStream]
/// 报告进度,返回结果不会自动修改 Floor 数据库。
static Future<FaceQualityAuditResult> auditFaceQuality({
required List<FaceQualityAuditItem> items,
}) async {
final map = await _channel.invokeMethod<dynamic>('auditFaceQuality', {
'items': items.map((item) => item.toMap()).toList(growable: false),
});
if (map is Map) return FaceQualityAuditResult.fromMap(map);
return const FaceQualityAuditResult(
results: [],
message: '原生未返回有效的质量检测结果',
);
}
/// 在虹软内存库中原子应用单样本新增、替换或删除。
///
/// 替换失败时原生层恢复旧特征;[operation] 仅允许 upsert 或 delete。
static Future<FaceDeltaApplyResult> applyFaceDelta({
required String operation,
required String sampleId,
String? userId,
String? username,
List<int>? featureData,
}) async {
final map = await _channel.invokeMethod<dynamic>('applyFaceDelta', {
'operation': operation,
'sampleId': int.tryParse(sampleId),
'userId': userId,
'username': username,
'featureData': featureData,
});
if (map is Map) return FaceDeltaApplyResult.fromMap(map);
return const FaceDeltaApplyResult(
success: false,
message: '原生未返回有效的人脸增量结果',
code: -99,
);
}
static Future<RegisterAllStorageFaceRes?> registerAllStorageFace({
required String filePath,
String? fileName,
}) async {
final pa = {
"filePath": filePath,
"fileName": fileName,
};
final map =
await _channel.invokeMethod<dynamic>('registerAllStorageFace', pa);
if (map == null) return null;
final encode = json.encode(map);
return RegisterAllStorageFaceRes.fromJson(json.decode(encode));
}
static Future<FaceRes?> getFace({
required int sampleId,
}) async {
final pa = {
"sampleId": sampleId,
};
final map = await _channel.invokeMethod<dynamic>('getFace', pa);
if (map == null) return null;
final encode = json.encode(map);
return FaceRes.fromJson(json.decode(encode));
}
static Future<int> getFaceCount() async {
final pa = {};
final res = await _channel.invokeMethod<int>('getFaceCount', pa);
if (res == null) return 0;
return res;
}
static Future<bool> removeFace({
required String sampleId,
}) async {
final sampleIdInt = int.tryParse(sampleId);
final pa = {
"sampleId": sampleIdInt,
};
final res = await _channel.invokeMethod<bool?>('removeFace', pa);
if (res == null) return false;
return res;
}
static Future<List<int>> registerFaceByDatabase({
String? userId,
}) async {
final pa = {
"userId": userId,
};
final res = await _channel.invokeMethod('registerFaceByDatabase', pa);
final errorSampleIdList = (res as List).cast<int>();
return errorSampleIdList;
}
static Stream<FaceStream?> get faceStream {
_stream ??= _eventChannel.receiveBroadcastStream().map((event) {
if (event == null) return null;
if (event == 'null') return null;
return FaceStream.fromJson(jsonDecode(event));
});
return _stream!;
}
static Stream<FaceRegistrationProgress> get registrationStream {
_registrationStream ??= _registrationEventChannel
.receiveBroadcastStream()
.where((event) => event is Map)
.map((event) => FaceRegistrationProgress.fromMap(event as Map));
return _registrationStream!;
}
/// 在库人脸质量检测进度广播流;订阅时会先收到原生最新状态。
static Stream<FaceQualityAuditProgress> get qualityAuditStream {
_qualityAuditStream ??= _qualityAuditEventChannel
.receiveBroadcastStream()
.where((event) => event is Map)
.map((event) => FaceQualityAuditProgress.fromMap(event as Map))
.asBroadcastStream();
return _qualityAuditStream!;
}
}