This commit is contained in:
dzc committed 2025-03-09 18:53:36 +08:00
commit 295b4058be
243 files changed
+19832

No files matched your search

@@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:common_utils/common_utils.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class SyncConfigTool {
static const _tag = "sync配置工具";
// 自动同步设备信息导本地缓存
static Future<void> autoSyncSysInfo() async {
final login = await AppUserTool.isLogin();
if (!login) return;
final sysSetting = await AppSettingTool.getSysSetting();
logger.i("|$_tag|开始同步设备信息 sysInfo:|${json.encode(sysSetting)}|");
if (ObjectUtil.isEmpty(sysSetting)) return;
if (ObjectUtil.isEmpty(sysSetting.serviceId)) return;
final serviceId = sysSetting.serviceId;
final pa = {"id": serviceId};
final res = await HardApi.get(pa);
if (res.code != 0) {
logger.e("|$_tag|同步设备信息错误serviceId:|$serviceId|;res:$res;");
return;
}
final data = res.data as ServiceHardware;
// 基本信息
await AppSettingTool.updateSysInfoByService(data);
// 人脸sdk
await autoSyncFaceSdk(detail: data, sysInfo: sysSetting);
logger.i("|$_tag|结束同步设备信息 sysInfo:|${json.encode(sysSetting)}|");
}
// 自动同步设备信息导本地缓存-同步人脸sdk
static Future<void> autoSyncFaceSdk({
required ServiceHardware detail,
required SysSetting sysInfo,
}) async {
final appId = detail.faceAppId;
final sdkKey = detail.faceSdkKey;
final activeKey = detail.faceActiveKey;
bool isUpdate = false;
if (appId != sysInfo.faceAppId) {
sysInfo.faceAppId = appId;
isUpdate = true;
}
if (sdkKey != sysInfo.faceSdkKey) {
sysInfo.faceSdkKey = sdkKey;
isUpdate = true;
}
if (activeKey != sysInfo.faceActiveKey) {
sysInfo.faceActiveKey = activeKey;
isUpdate = true;
}
if (!isUpdate) return;
final activeSdk = await AppFaceTool.isActiveSdk();
await AppSettingTool.setSysSetting(sysInfo);
logger.i("|$_tag||同步人脸sdk参数自动激活${activeSdk ? '成功' : '失败'}");
}
}
+193
View File
@@ -0,0 +1,193 @@
import 'dart:io';
import 'package:common_utils/common_utils.dart';
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
class SyncFaceTool {
static const _tag = "sync人脸工具";
static Future<bool> syncGroupFace({
required int pageNo,
required String merchantDeptId,
required String storagePath,
String? userId,
bool? isGlobalFace,
}) async {
final app = Get.find<AppControl>();
final faceDao = app.dataBase.getFaceDao;
const size = 20;
var pa = {
"current": pageNo,
"size": size,
"userId": userId,
"merchantDeptId": merchantDeptId,
};
if (isGlobalFace != null && isGlobalFace) pa["merchantDeptId"] = null;
logger.i("|$_tag|同步人脸api参数:|$pa|");
try {
final res = await UserApi.getFaceList(pa);
if (res.code != 0 || res.data == null) throw Error();
final data = res.data as List<ServiceAsyncFace>;
if (ObjectUtil.isEmptyList(data)) {
if (ObjectUtil.isEmpty(userId)) return false;
// 删除人脸图片
final faceList = await faceDao.findByUserId(userId!);
if (!ObjectUtil.isEmptyList(faceList)) {
for (final v in faceList!) {
if (ObjectUtil.isNotEmpty(v.path)) {
await delFaceByStorage(filePath: v.path!);
await AppFaceTool.removeFace(sampleId: v.sampleId);
}
}
}
// 删除数据库
await faceDao.delByUserId(userId);
return false;
}
if (isGlobalFace != null && !isGlobalFace) return false;
if (userId != null) {
final dbUserList = await faceDao.findByUserId(userId);
if (!ObjectUtil.isEmptyList(dbUserList)) {
for (final v in dbUserList!) {
final target = data.firstWhereOrNull((i) => i.id == v.sampleId);
if (target == null) {
if (ObjectUtil.isNotEmpty(v.path)) {
await delFaceByStorage(filePath: v.path!);
await AppFaceTool.removeFace(sampleId: v.sampleId);
}
await faceDao.delById(v.id!);
}
}
}
}
for (final v in data) {
final faceEntity = await faceDao.findBySampleId(v.autoCode!);
if (faceEntity == null) {
final add = FaceEntity(
userId: v.userId!,
sampleId: v.autoCode!,
userName: v.username ?? "",
deptId: v.deptId!,
deptName: v.deptName ?? "",
imagePath: v.faceImg ?? "",
createTime: DateTime.now(),
);
final addId = await faceDao.save(add);
if (addId != null) {
String? fileName = generateFaceFileName(
userId: add.userId,
sampleId: add.sampleId,
username: add.userName,
facePath: add.imagePath,
);
final filePath = await writeFaceByStorage(
storagePath: storagePath,
fileName: fileName,
facePath: add.imagePath,
);
if (ObjectUtil.isNotEmpty(filePath)) {
add.id = addId;
add.updateTime = DateTime.now();
add.path = filePath;
await faceDao.edit(add);
}
// 重新生成人脸模型
await AppFaceTool.removeFace(sampleId: add.sampleId);
await AppFaceTool.faceRegister(
userId: add.userId.toString(),
forceRegister: true,
);
}
continue;
}
final edit = FaceEntity(
id: faceEntity.id!,
userId: v.userId!,
sampleId: v.autoCode!,
userName: v.username ?? "",
deptId: v.deptId!,
deptName: v.deptName ?? "",
imagePath: v.faceImg ?? "",
createTime: DateTime.now(),
);
if (edit == faceEntity) {
continue;
}
String? fileName = generateFaceFileName(
userId: edit.userId,
sampleId: edit.sampleId,
username: edit.userName,
facePath: edit.imagePath,
);
final filePath = await writeFaceByStorage(
storagePath: storagePath,
facePath: edit.imagePath,
fileName: fileName,
);
if (ObjectUtil.isNotEmpty(filePath)) {
if (ObjectUtil.isNotEmpty(edit.path)) {
await delFaceByStorage(filePath: edit.path!);
}
edit.path = filePath;
}
await faceDao.edit(edit);
// 重新生成人脸模型
await AppFaceTool.removeFace(sampleId: edit.sampleId);
await AppFaceTool.faceRegister(
userId: edit.userId.toString(),
forceRegister: true,
);
}
if (data.length >= size) return true;
} catch (e, stack) {
logger.e("|$_tag|同步人脸意外的错误error:|$e|stack:|$stack|");
}
return false;
}
/// 删除人脸图片
static Future<void> delFaceByStorage({
required String filePath,
}) async {
try {
final file = File(filePath);
if (await file.exists()) {
await file.delete();
}
} catch (e) {
logger.e("|$_tag|删除人脸图片意外的错误error:|$e|");
}
}
static String? generateFaceFileName({
required String userId,
required String sampleId,
required String username,
required String facePath,
}) {
if (ObjectUtil.isEmpty(facePath)) return null;
return "${userId}_${sampleId}_$username$facePath";
}
/// 写入人脸头像到本地磁盘
static Future<String?> writeFaceByStorage({
required String storagePath,
required String? fileName,
required String facePath,
}) async {
if (ObjectUtil.isEmpty(fileName)) return null;
final storageDirectory = Directory(storagePath);
final file = File('${storageDirectory.path}/$fileName');
if (await file.exists()) {
await file.delete();
}
final bytesImg = await AppNetworkTool.downloadImage(facePath);
if (bytesImg == null) return null;
final writeFile = await file.writeAsBytes(bytesImg);
if (await writeFile.exists()) {
return file.path;
}
return null;
}
}
@@ -0,0 +1,95 @@
import 'package:common_utils/common_utils.dart';
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class SyncOrderTool {
static const _tag = "sync订单工具";
// 同步离线订单
static Future<void> syncOfflineOrder() async {
final app = Get.find<AppControl>();
final sysSetting = await AppSettingTool.getSysSetting();
final sysType = sysSetting.sysType.name;
final orderDao = app.dataBase.getOrderDao;
// 查询离线订单
final startDate = DateTime.now().subtract(const Duration(days: 30));
final endDate = DateTime.now().add(const Duration(days: 1));
final list = await orderDao.findOffline(
sysType,
startDate,
endDate,
);
if (ObjectUtil.isEmptyList(list)) {
logger.i("|$_tag|错误,没有需要同步的离线订单");
return;
}
final chunkList = chunkArray(list, 5);
for (var v in chunkList) {
await _syncOfflineGroupOrder(v);
}
// 更新同步时间
sysSetting.offlineOrderSyncTime = DateTime.now();
await AppSettingTool.setSysSetting(sysSetting);
}
// 同步一组离线订单
static Future<void> _syncOfflineGroupOrder(List<OrderEntity> list) async {
final app = Get.find<AppControl>();
final sysSetting = await AppSettingTool.getSysSetting();
final serviceId = sysSetting.serviceId;
final serviceMerchantId = sysSetting.serviceMerchantId;
final shopCardDao = app.dataBase.getShopCardDao;
List<Map<String, dynamic>> uploadList = [];
for (final OrderEntity v in list) {
Map<String, dynamic> item = v.toJson();
if (v.id != null) {
final carList = await shopCardDao.findByOrderId(v.id!);
if (!ObjectUtil.isEmptyList(carList)) {
item["carList"] = carList.map((i) => i.toJson()).toList();
}
}
uploadList.add(item);
}
final pa = {
"list": uploadList,
"hardwareId": serviceId,
"merchantId": serviceMerchantId!,
};
logger.i("|$_tag|同步参数:|$pa|");
try {
final res = await HardApi.syncOfflineOrder(pa);
if (res.code != 0 || res.data == null) throw Error();
final data = res.data as List<dynamic>;
final batchEditCount = await handEditOfflineStatus(data);
logger.i("|$_tag|同步成功的数量为:|$batchEditCount|ids为:|$data|");
} catch (e) {
logger.e("|$_tag|同步一组离线订单意外的错误error:$e");
}
}
static Future<int> handEditOfflineStatus(List<dynamic> codes) async {
final app = Get.find<AppControl>();
final placeholders = List.generate(codes.length, (index) => '?').join(', ');
final sql = '''
UPDATE order_record SET status = ? WHERE uniqueCode IN ($placeholders)
''';
final database = app.dataBase.database;
return await database.rawUpdate(sql, [OrderStatus.pay.name, ...codes]);
}
// 切割数组
static List<List<T>> chunkArray<T>(List<T> array, int chunkSize) {
if (chunkSize <= 0) return [];
List<List<T>> chunks = [];
for (var i = 0; i < array.length; i += chunkSize) {
int end = (i + chunkSize < array.length) ? i + chunkSize : array.length;
chunks.add(array.sublist(i, end));
}
return chunks;
}
}
@@ -0,0 +1,94 @@
import 'package:common_utils/common_utils.dart';
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class SyncPrintTool {
static const _tag = "sync打印工具";
static bool _isPrint = false;
static Future<void> addTask({
required String orderId,
required String androidOrderId,
required String historyId,
required String pic,
}) async {
final app = Get.find<AppControl>();
final printHistoryDao = app.dataBase.getPrintHistoryDao;
final saveObj = PrintHistoryEntity(
orderId: androidOrderId,
serviceOrderId: orderId,
servicePrintId: historyId,
invoiceUrl: pic,
status: PrintStatus.none,
createTime: DateTime.now(),
);
await printHistoryDao.save(saveObj);
logger.i("|$_tag|添加到打印记录到本地数据库,json:|${saveObj.toJson()}|");
await SyncPrintTool.doCheckPrint();
}
// 检查打印
static Future<void> doCheckPrint() async {
if (_isPrint) {
logger.i("|$_tag|检查打印,正在打印中");
return;
}
final app = Get.find<AppControl>();
final printHistoryDao = app.dataBase.getPrintHistoryDao;
final printFacility = app.printManage;
try {
// 检查有没有需要打印的
final list = await printHistoryDao.getAll(PrintStatus.none.name);
if (ObjectUtil.isEmptyList(list)) {
_isPrint = false;
logger.i("|$_tag|检查打印,没有需要打印的发票");
return;
}
final isOpen = await printFacility.open();
if (!isOpen) {
_isPrint = false;
logger.e("|$_tag|检查打印,打开打印机失败");
return;
}
for (final v in list) {
await doPrint(v);
}
} catch (e) {
logger.e("|$_tag|检查打印,打印错误,error:|$e|");
} finally {
_isPrint = false;
}
}
// 执行打印
static Future<void> doPrint(PrintHistoryEntity v) async {
final app = Get.find<AppControl>();
final printHistoryDao = app.dataBase.getPrintHistoryDao;
final printFacility = app.printManage;
try {
final isPrint = await printFacility.printImg(v.invoiceUrl);
if (!isPrint) {
v.status = PrintStatus.error;
await printHistoryDao.edit(v);
throw Error();
}
// 更新本地数据库
v.status = PrintStatus.success;
v.printTime = DateUtil.formatDate(DateTime.now());
await printHistoryDao.edit(v);
// 同步到服务端
final printPa = {
"orderId": v.serviceOrderId,
"historyId": v.servicePrintId,
};
await HardApi.printInvoice(printPa);
v.status = PrintStatus.sync;
v.syncTime = DateTime.now();
await printHistoryDao.edit(v);
} catch (e) {
logger.i("|$_tag|执行打印,打印错误,error:|$e|");
}
}
}
@@ -0,0 +1,275 @@
import 'package:common_utils/common_utils.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class SyncProductTool {
static const _tag = "sync商品工具";
static Future<void> updateDatabase() async {
final isNetwork = AppNetworkTool.isAutoNetwork();
if (!isNetwork) return;
EasyLoading.show(
status: '初始化资源...',
maskType: EasyLoadingMaskType.black,
);
await syncClassify();
await syncProduct();
EasyLoading.dismiss();
}
/// 同步商品分类
static Future<void> syncClassify() async {
final sysSetting = await AppSettingTool.getSysSetting();
final sysType = sysSetting.sysType;
try {
List<ServiceClassify>? apiList;
if (sysType == SysType.canteen) {
apiList = await ProductApi.cookbookClassify(null);
} else if (sysType == SysType.supermarket) {
apiList = await ProductApi.supermarketClassify(null);
}
if (apiList == null) return;
if (ObjectUtil.isEmptyList(apiList)) {
return;
}
await _batchUpdateClassify(list: apiList, sysType: sysSetting.sysType);
// 更新同步时间
if (sysType == SysType.canteen) {
sysSetting.canteenClassifySyncTime = DateTime.now();
}
if (sysType == SysType.supermarket) {
sysSetting.supermarketClassifySyncTime = DateTime.now();
}
await AppSettingTool.setSysSetting(sysSetting);
} catch (e, stack) {
logger.e("|$_tag|同步|${sysType.title}|分类信息意外的错误error:|$e|,stack:|$stack|");
}
}
static Future<void> _batchUpdateClassify({
required List<ServiceClassify> list,
required SysType sysType,
}) async {
final app = Get.find<AppControl>();
final dataBase = app.dataBase;
final classifyDao = dataBase.getClassifyDao;
final classifyList = await classifyDao.getAll(sysType.name, "-1");
if (ObjectUtil.isEmpty(classifyList)) {
await _batchAddOrUpdateClassify(
list: list,
sysType: sysType,
isUpdate: false,
);
return;
}
final apiServiceCodes = list.map((e) => e.id.toString()).toSet();
final dbServiceCodes = classifyList.map((e) => e.serviceUniqueCode).toSet();
final addCodes = apiServiceCodes.difference(dbServiceCodes).toList();
final deleteCodes = dbServiceCodes.difference(apiServiceCodes).toList();
final updateCodes = apiServiceCodes.intersection(dbServiceCodes).toList();
if (ObjectUtil.isNotEmpty(addCodes)) {
await _batchAddOrUpdateClassify(
list: _filterClassifyListByCodes(list, addCodes),
sysType: sysType,
isUpdate: false,
);
}
if (ObjectUtil.isNotEmpty(deleteCodes)) {
await classifyDao.delByIds(deleteCodes);
}
if (ObjectUtil.isNotEmpty(updateCodes)) {
await _batchAddOrUpdateClassify(
list: _filterClassifyListByCodes(list, updateCodes),
sysType: sysType,
isUpdate: true,
);
}
}
static Future<void> _batchAddOrUpdateClassify({
required List<ServiceClassify> list,
required SysType sysType,
required bool isUpdate,
}) async {
if (ObjectUtil.isEmpty(list)) return;
final app = Get.find<AppControl>();
final dataBase = app.dataBase;
final classifyDao = dataBase.getClassifyDao;
List<ClassifyEntity> changeList = [];
for (final v in list) {
ClassifyEntity item = ClassifyEntity(
serviceUniqueCode: v.id.toString(),
sysType: sysType,
name: v.name!,
categoryType: v.categoryType ?? "-1",
sort: v.sort ?? 999,
createTime: DateTime.now(),
updateTime: DateTime.now(),
delFlag: false,
);
if (isUpdate) {
final detail = await classifyDao.getByCode(item.serviceUniqueCode);
if (ObjectUtil.isEmpty(detail)) continue;
if (detail == item) continue;
item.id = detail?.id;
}
changeList.add(item);
}
if (ObjectUtil.isNotEmpty(changeList)) {
isUpdate
? await classifyDao.batchEdit(changeList)
: await classifyDao.batchSave(changeList);
}
}
static List<ServiceClassify> _filterClassifyListByCodes(
List<ServiceClassify> sourceList,
List<String> addCodes,
) {
return sourceList
.where((item) => addCodes.contains(item.id.toString()))
.toList();
}
/// 同步商品
static Future<void> syncProduct() async {
final sysSetting = await AppSettingTool.getSysSetting();
final sysType = sysSetting.sysType;
try {
List<ServiceProduct>? apiList;
if (sysType == SysType.canteen) {
final day = DateUtil.formatDate(
DateTime.now(),
format: DateFormats.y_mo_d,
);
final pa = {
"day": day,
"hourType": null,
"categoryId": null,
"merchantId": sysSetting.serviceMerchantId,
};
apiList = await ProductApi.canteenProduct(pa);
} else if (sysType == SysType.supermarket) {
apiList = await ProductApi.supermarketProduct(null);
}
if (apiList == null) return;
if (ObjectUtil.isEmptyList(apiList)) {
return;
}
// 更新数据
await _batchUpdateProduct(list: apiList, sysType: sysSetting.sysType);
// 更新同步时间
if (sysType == SysType.canteen) {
sysSetting.canteenProductSyncTime = DateTime.now();
}
if (sysType == SysType.supermarket) {
sysSetting.supermarketProductSyncTime = DateTime.now();
}
await AppSettingTool.setSysSetting(sysSetting);
} catch (e, stack) {
logger.e("|$_tag|同步|${sysType.title}|商品信息意外的错误error:|$e|,stack:|$stack|");
}
}
static Future<void> _batchUpdateProduct({
required List<ServiceProduct> list,
required SysType sysType,
}) async {
final app = Get.find<AppControl>();
final dataBase = app.dataBase;
final productDao = dataBase.getProductDao;
final productList = await productDao.getAll(
"-1",
"-1",
"-1",
);
if (ObjectUtil.isEmpty(productList)) {
await _batchAddOrUpdateProduct(
list: list,
sysType: sysType,
isUpdate: false,
);
return;
}
final apiServiceCodes = list.map((e) => e.spuId.toString()).toSet();
final dbServiceCodes = productList.map((e) => e.serviceUniqueCode).toSet();
final addCodes = apiServiceCodes.difference(dbServiceCodes).toList();
final deleteCodes = dbServiceCodes.difference(apiServiceCodes).toList();
final updateCodes = apiServiceCodes.intersection(dbServiceCodes).toList();
if (ObjectUtil.isNotEmpty(addCodes)) {
await _batchAddOrUpdateProduct(
list: _filterProductListByCodes(list, addCodes),
sysType: sysType,
isUpdate: false,
);
}
if (ObjectUtil.isNotEmpty(deleteCodes)) {
await productDao.delByIds(deleteCodes);
}
if (ObjectUtil.isNotEmpty(updateCodes)) {
await _batchAddOrUpdateProduct(
list: _filterProductListByCodes(list, updateCodes),
sysType: sysType,
isUpdate: true,
);
}
}
static Future<void> _batchAddOrUpdateProduct({
required List<ServiceProduct> list,
required SysType sysType,
required bool isUpdate,
}) async {
if (ObjectUtil.isEmpty(list)) return;
final app = Get.find<AppControl>();
final dataBase = app.dataBase;
final productDao = dataBase.getProductDao;
List<ProductEntity> changeList = [];
for (final v in list) {
ProductEntity item = ProductEntity(
serviceUniqueCode: sysType == SysType.canteen
? v.spuId.toString()
: v.spuId.toString(),
sysType: sysType,
classifyServiceUniqueCode:
v.categoryId != null ? v.categoryId.toString() : "-1",
name: v.name!,
price: v.price ?? 0,
image: v.picUrl,
status: 1,
sort: v.sort ?? 999,
createTime: DateTime.now(),
updateTime: DateTime.now(),
hourType: ProductHourType.values.fromCode(v.hourType),
isDefault: false,
delFlag: false,
);
if (isUpdate) {
final detail = await productDao.getByCode(item.serviceUniqueCode);
if (ObjectUtil.isEmpty(detail)) continue;
if (detail == item) continue;
item.id = detail?.id;
}
changeList.add(item);
}
if (ObjectUtil.isNotEmpty(changeList)) {
isUpdate
? await productDao.batchEdit(changeList)
: await productDao.batchSave(changeList);
}
}
static List<ServiceProduct> _filterProductListByCodes(
List<ServiceProduct> sourceList,
List<String> codes,
) {
return sourceList
.where((item) => codes.contains(item.spuId.toString()))
.toList();
}
}
+82
View File
@@ -0,0 +1,82 @@
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class SyncUserTool {
static const _tag = "sync商品工具";
// 获取存储用户
static Future<UserEntity?> storageUserByCarNumber(String cardNumber) async {
final app = Get.find<AppControl>();
final userDao = app.dataBase.getUserDao;
return await userDao.findByCardNumber(cardNumber);
}
// 获取存储用户
static Future<UserEntity?> storageUserByUserId(String userId) async {
final app = Get.find<AppControl>();
final userDao = app.dataBase.getUserDao;
return await userDao.findByUserId(userId);
}
/// 存储用户
static Future<void> storageUser({required ServiceUser user}) async {
try {
final app = Get.find<AppControl>();
final userDao = app.dataBase.getUserDao;
final detail = await userDao.findByUserId(user.userId!);
if (detail == null) {
final obj = _parseStorageUser(user: user, createDate: true);
await userDao.save(obj);
logger.i("|$_tag|,存储用户,新增成功|${obj.toJson()}|");
} else {
final obj = _parseStorageUser(user: user, updateDate: true);
obj.id = detail.id;
await userDao.edit(obj);
logger.i("|$_tag|,存储用户,修改成功|${obj.toJson()}|");
}
} catch (e, stack) {
logger.e("|$_tag|,存储用户失败意外的错误error:|$e|stack:|$stack|");
}
}
static ServiceUser parsePayUser({
required UserEntity user,
}) {
return ServiceUser(
userId: user.userId,
username: user.nickname,
deptId: null,
userMobile: user.mobile,
userAvatar: user.avatar,
walletId: user.walletId,
walletTypeId: user.walletTypeId,
walletBalance: user.walletBalance,
walletIsFreeze: false,
walletCardNumber: user.walletCardNumber,
);
}
static UserEntity _parseStorageUser({
required ServiceUser user,
bool createDate = false,
bool updateDate = false,
}) {
final userEntity = UserEntity(
userId: user.userId ?? "-1",
nickname: user.username ?? "未知",
mobile: user.userMobile,
avatar: user.userAvatar,
walletCardNumber: user.walletCardNumber,
walletId: user.walletId,
walletTypeId: user.walletTypeId,
walletBalance: user.walletBalance,
);
if (createDate) {
userEntity.createTime = DateTime.now();
}
if (updateDate) {
userEntity.updateTime = DateTime.now();
}
return userEntity;
}
}