feat:人脸管理优化
This commit is contained in:
1 parent
88e73ad950
commit
2490d4f9a0
9 files changed
+175
-40
No files matched your search
@@ -1,5 +1,17 @@
|
||||
package com.example.pj_supermarket_cloud
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugins.facediscern.flutter.FlutterFaceDiscernPlugin
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
class MainActivity : FlutterActivity() {
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
// 人脸识别插件
|
||||
val flutterFaceDiscernPlugin = FlutterFaceDiscernPlugin()
|
||||
flutterEngine.plugins.add(flutterFaceDiscernPlugin)
|
||||
// tts插件
|
||||
|
||||
// 箭头全局键盘插件
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ class MainApp extends StatelessWidget {
|
||||
final fTheme = appTheme.theme.value;
|
||||
final sizeScalar = appTheme.sizeScalar.value;
|
||||
final themeData = FThemeData.inherit(
|
||||
colorScheme: fTheme.colorScheme.copyWith(),
|
||||
colorScheme: fTheme.colorScheme,
|
||||
typography: fTheme.typography.scale(sizeScalar: sizeScalar),
|
||||
style: fTheme.style.copyWith(
|
||||
borderRadius: BorderRadius.all(
|
||||
|
||||
@@ -164,5 +164,16 @@ abstract class AppFileTool {
|
||||
}
|
||||
}
|
||||
|
||||
// 备份日志
|
||||
// 获取文件后缀
|
||||
static String getFileExtension(String url) {
|
||||
int lastDotIndex = url.lastIndexOf('.');
|
||||
if (lastDotIndex == -1) {
|
||||
return '';
|
||||
}
|
||||
int lastSlashIndex = url.lastIndexOf('/');
|
||||
if (lastDotIndex > lastSlashIndex) {
|
||||
return url.substring(lastDotIndex);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,11 @@ abstract class FaceDiscernPlugs {
|
||||
static const String _tag = "人脸识别plug";
|
||||
|
||||
static const MethodChannel _channel = MethodChannel(
|
||||
"local/face_discern",
|
||||
Constants.facePlugsMethodChannel,
|
||||
);
|
||||
|
||||
static const EventChannel _eventChannel = EventChannel(
|
||||
"local/face_discern_event",
|
||||
Constants.facePlugsEventChannel,
|
||||
);
|
||||
|
||||
static MethodChannel get getChannel => _channel;
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:common_utils/common_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:pj_supermarket_cloud/common/index.dart';
|
||||
|
||||
class SyncFaceTool {
|
||||
static const _tag = "sync人脸工具";
|
||||
|
||||
// 同步人脸
|
||||
static Future<void> syncFace({
|
||||
bool isAll = false,
|
||||
VoidCallback? onSyncGroup,
|
||||
}) async {
|
||||
final appSetting = await AppSettingTool.getSysSetting();
|
||||
final merchantDeptId = appSetting.merchantDeptId;
|
||||
// 人脸存储路径
|
||||
final storagePath = await AppFileTool.getFacePath();
|
||||
if (ObjectUtil.isEmpty(storagePath)) {
|
||||
logger.e("|$_tag|同步人脸错误,创建人脸存储路径错误");
|
||||
return;
|
||||
}
|
||||
// 同步没有的人脸
|
||||
int pageNo = 1;
|
||||
bool isNext = true;
|
||||
while (isNext) {
|
||||
final bool = await syncGroupFace(
|
||||
pageNo: pageNo,
|
||||
merchantDeptId: isAll ? "" : merchantDeptId!,
|
||||
storagePath: storagePath!,
|
||||
);
|
||||
if (isNext != bool) isNext = bool;
|
||||
if (!bool) break;
|
||||
pageNo += 1;
|
||||
onSyncGroup?.call();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> syncGroupFace({
|
||||
required int pageNo,
|
||||
required String merchantDeptId,
|
||||
@@ -16,7 +47,7 @@ class SyncFaceTool {
|
||||
}) async {
|
||||
final app = Get.find<AppControl>();
|
||||
final faceDao = app.dataBase.getFaceDao;
|
||||
const size = 20;
|
||||
const size = 50;
|
||||
var pa = {
|
||||
"current": pageNo,
|
||||
"size": size,
|
||||
@@ -166,7 +197,7 @@ class SyncFaceTool {
|
||||
required String facePath,
|
||||
}) {
|
||||
if (ObjectUtil.isEmpty(facePath)) return null;
|
||||
return "${userId}_${sampleId}_$username$facePath";
|
||||
return "${userId}_${sampleId}_$username${AppFileTool.getFileExtension(facePath)}";
|
||||
}
|
||||
|
||||
/// 写入人脸头像到本地磁盘
|
||||
|
||||
@@ -20,6 +20,12 @@ class Constants {
|
||||
/// ttl android 插件方法管道
|
||||
static const localTTSPlugsMethodChannel = 'local/tts';
|
||||
|
||||
/// 人脸识别 插件方法管道
|
||||
static const facePlugsMethodChannel = 'local/face_discern';
|
||||
|
||||
/// 人脸识别 插件回调管道
|
||||
static const facePlugsEventChannel = 'local/face_discern_event';
|
||||
|
||||
/// 数据库名字
|
||||
static const databaseName = 'cash_register.db';
|
||||
}
|
||||
@@ -56,8 +56,10 @@ class PanelController extends GetxController {
|
||||
Get.toNamed(RouteNames.orderHistory);
|
||||
break;
|
||||
case PanelType.setting:
|
||||
final flag = await settingPasswordDialog(Get.context!);
|
||||
if (!flag) return;
|
||||
if (AppSysTool.isProd()) {
|
||||
final flag = await settingPasswordDialog(Get.context!);
|
||||
if (!flag) return;
|
||||
}
|
||||
Get.toNamed(RouteNames.setting);
|
||||
break;
|
||||
case PanelType.cardActive:
|
||||
|
||||
@@ -15,7 +15,7 @@ Future<void> _bindServiceDialog(BuildContext context, SysSetting sys) async {
|
||||
if (ObjectUtil.isNotEmpty(sys.serviceId)) {
|
||||
uniqueCodeC.text = sys.serviceId ?? "";
|
||||
} else if (AppSysTool.isDev()) {
|
||||
uniqueCodeC.text = "1896157295523049473";
|
||||
uniqueCodeC.text = "1894303392833716226";
|
||||
}
|
||||
await showAdaptiveDialog(
|
||||
context: context,
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:pj_supermarket_cloud/common/index.dart';
|
||||
class FaceManageController extends GetxController {
|
||||
static const String _tag = "人脸管理 Controller";
|
||||
|
||||
final appC = Get.find<AppControl>();
|
||||
|
||||
RxList<FaceEntity> faceList = RxList();
|
||||
|
||||
RxInt currentPage = RxInt(1);
|
||||
@@ -45,19 +47,16 @@ class FaceManageController extends GetxController {
|
||||
);
|
||||
}
|
||||
try {
|
||||
final app = Get.find<AppControl>();
|
||||
final faceDao = app.dataBase.getFaceDao;
|
||||
final faceDao = appC.dataBase.getFaceDao;
|
||||
String userName = usernameC.text;
|
||||
if (ObjectUtil.isEmpty(userName)) userName = "-1";
|
||||
final findCount = await faceDao.findCount(userName);
|
||||
totalPage.value = findCount ?? 0;
|
||||
final list = await faceDao.findAll(
|
||||
userName,
|
||||
pageSize.value,
|
||||
(currentPage.value - 1) * pageSize.value,
|
||||
);
|
||||
if (findCount != null) {
|
||||
totalPage.value = (findCount / pageSize.value).ceil();
|
||||
}
|
||||
faceList.value = list;
|
||||
} finally {
|
||||
if (isLoading) {
|
||||
@@ -78,14 +77,14 @@ class FaceManageController extends GetxController {
|
||||
Toast.error(msg: "请先绑定设备");
|
||||
return;
|
||||
}
|
||||
final isActive = await AppFaceTool.isActiveSdk();
|
||||
if (!isActive) {
|
||||
Toast.error(msg: "人脸sdk激活失败");
|
||||
return;
|
||||
}
|
||||
// final isActive = await AppFaceTool.isActiveSdk();
|
||||
// if (!isActive) {
|
||||
// Toast.error(msg: "人脸sdk激活失败");
|
||||
// return;
|
||||
// }
|
||||
final isSure = await appMessageDialog(
|
||||
Get.context!,
|
||||
"是否同步所有人脸,这个过程肯能比较长",
|
||||
"是否同步所有人脸,这个过程可能比较长",
|
||||
);
|
||||
if (!isSure) return;
|
||||
EasyLoading.show(
|
||||
@@ -93,10 +92,18 @@ class FaceManageController extends GetxController {
|
||||
maskType: EasyLoadingMaskType.black,
|
||||
);
|
||||
try {
|
||||
// await SyncFaceTool.syncFace(isAll: isAll);
|
||||
await SyncFaceTool.syncFace(
|
||||
isAll: isAll,
|
||||
onSyncGroup: () {
|
||||
currentPage.value = 1;
|
||||
getFaceList();
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
currentPage.value = 1;
|
||||
getFaceList();
|
||||
await AppFaceTool.faceRegister(forceRegister: true);
|
||||
Toast.success(msg: "同步成功");
|
||||
EasyLoading.dismiss();
|
||||
}
|
||||
}
|
||||
@@ -112,23 +119,22 @@ class FaceManageController extends GetxController {
|
||||
maskType: EasyLoadingMaskType.black,
|
||||
);
|
||||
try {
|
||||
final app = Get.find<AppControl>();
|
||||
final faceDao = app.dataBase.getFaceDao;
|
||||
final faceDao = appC.dataBase.getFaceDao;
|
||||
await faceDao.delAll();
|
||||
final bool = await AppFaceTool.removeFace(sampleId: "-1");
|
||||
logger.i(bool);
|
||||
} finally {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
getFaceList();
|
||||
Toast.success(msg: "同步成功");
|
||||
EasyLoading.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_listenMqCallback() {
|
||||
final app = Get.find<AppControl>();
|
||||
mqUpdateListen =
|
||||
app.mqUpdateEventBus.on<MqtActionEnum>().listen((event) async {
|
||||
appC.mqUpdateEventBus.on<MqtActionEnum>().listen((event) async {
|
||||
switch (event) {
|
||||
case MqtActionEnum.face:
|
||||
getFaceList(isLoading: true);
|
||||
@@ -142,6 +148,30 @@ class FaceManageController extends GetxController {
|
||||
if (page != null) currentPage.value = page;
|
||||
await getFaceList(isLoading: true);
|
||||
}
|
||||
|
||||
// 删除人脸
|
||||
Future<void> deleteFace(FaceEntity v) async {
|
||||
final isSure = await appMessageDialog(Get.context!, "是否删除人脸");
|
||||
if (!isSure) return;
|
||||
EasyLoading.show(
|
||||
status: '删除中...',
|
||||
maskType: EasyLoadingMaskType.black,
|
||||
);
|
||||
try {
|
||||
final faceDao = appC.dataBase.getFaceDao;
|
||||
if (ObjectUtil.isNotEmpty(v.path)) {
|
||||
await SyncFaceTool.delFaceByStorage(filePath: v.path!);
|
||||
}
|
||||
await faceDao.delById(v.id!);
|
||||
await AppFaceTool.removeFace(sampleId: v.sampleId);
|
||||
} finally {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
getFaceList();
|
||||
Toast.success(msg: "删除成功");
|
||||
EasyLoading.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索
|
||||
@@ -202,28 +232,70 @@ Widget buildFaceSearch(BuildContext context) {
|
||||
|
||||
/// 列表
|
||||
Widget buildFaceList(context) {
|
||||
return <Widget>[].toRow();
|
||||
}
|
||||
|
||||
/// 分页
|
||||
Widget buildFacePage(context) {
|
||||
final faceC = Get.find<FaceManageController>();
|
||||
return appPage(
|
||||
context,
|
||||
pageNo: faceC.currentPage.value,
|
||||
pageSize: faceC.pageSize.value,
|
||||
total: faceC.totalPage.value,
|
||||
onPageChanged: (pageNo) => faceC.doPage(page: pageNo),
|
||||
final theme = FTheme.of(context);
|
||||
final productC = Get.find<FaceManageController>();
|
||||
return Obx(
|
||||
() => GridView.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 5,
|
||||
mainAxisSpacing: 10.w,
|
||||
crossAxisSpacing: 10.w,
|
||||
childAspectRatio: 1.2,
|
||||
),
|
||||
itemCount: productC.faceList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final v = productC.faceList[index];
|
||||
return FCard(
|
||||
style: theme.cardStyle.copyWith(
|
||||
contentStyle: theme.cardStyle.contentStyle.copyWith(
|
||||
padding: EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
image: appImg(v.path, width: 120.w, height: 120.w)
|
||||
.center()
|
||||
.marginOnly(top: 5.h),
|
||||
title: Text(
|
||||
v.userName,
|
||||
maxLines: 1,
|
||||
style: theme.typography.sm,
|
||||
).center(),
|
||||
subtitle: <Widget>[
|
||||
Text(
|
||||
"用户编号:${v.userId}",
|
||||
style: theme.typography.xs.copyWith(height: 1),
|
||||
),
|
||||
Text(
|
||||
"样本编号:${v.sampleId}",
|
||||
style: theme.typography.xs.copyWith(height: 1),
|
||||
),
|
||||
]
|
||||
.toColumnSpace(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
)
|
||||
.width(double.infinity)
|
||||
.marginOnly(top: 5.h),
|
||||
).onTap(() => productC.deleteFace(v));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget settingFace(BuildContext context) {
|
||||
Get.put<FaceManageController>(
|
||||
final faceC = Get.put<FaceManageController>(
|
||||
FaceManageController(),
|
||||
);
|
||||
return <Widget>[
|
||||
buildFaceSearch(context),
|
||||
buildFaceList(context).expanded(),
|
||||
buildFacePage(context),
|
||||
Obx(
|
||||
() => appPage(
|
||||
context,
|
||||
pageNo: faceC.currentPage.value,
|
||||
pageSize: faceC.pageSize.value,
|
||||
total: faceC.totalPage.value,
|
||||
onPageChanged: (pageNo) => faceC.doPage(page: pageNo),
|
||||
),
|
||||
),
|
||||
].toColumn();
|
||||
}
|
||||
Reference in new issue
Block a user