feat:预定广播
This commit is contained in:
1 parent
fa90df8d9d
commit
691fb68d53
13 files changed
+341
-1
No files matched your search
@@ -8,6 +8,7 @@ enum MqtActionEnum implements BaseEnum {
|
||||
SUPERMARKET_CLASSIFY("50", "超市分类变化"),
|
||||
PRINT("60", "打印变化"),
|
||||
FACE("70", "人脸变化"),
|
||||
TAKE_FOOD_BROADCAST("80", "定餐广播"),
|
||||
;
|
||||
|
||||
@override
|
||||
|
||||
@@ -61,3 +61,54 @@ class MqttInvoiceMsg {
|
||||
|
||||
Map<String, dynamic> toJson() => _$MqttInvoiceMsgToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class MqttTakeFoodBroadcastMsg {
|
||||
/// 发送硬件的topic
|
||||
String topic;
|
||||
|
||||
/// 用户id
|
||||
String userId;
|
||||
|
||||
/// 用户id
|
||||
String username;
|
||||
|
||||
/// 用户id
|
||||
String userMobile;
|
||||
|
||||
/// 广播id
|
||||
String id;
|
||||
|
||||
/// 预定标题
|
||||
String title;
|
||||
|
||||
/// 广播内容
|
||||
String content;
|
||||
|
||||
/// 最大广播次数
|
||||
int broadcastMaxCount;
|
||||
|
||||
/// 商品介绍
|
||||
String productDesc;
|
||||
|
||||
/// 支付价格
|
||||
int payPrice;
|
||||
|
||||
MqttTakeFoodBroadcastMsg({
|
||||
required this.topic,
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.userMobile,
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
required this.broadcastMaxCount,
|
||||
required this.productDesc,
|
||||
required this.payPrice,
|
||||
});
|
||||
|
||||
factory MqttTakeFoodBroadcastMsg.fromJson(Map<String, dynamic> json) =>
|
||||
_$MqttTakeFoodBroadcastMsgFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$MqttTakeFoodBroadcastMsgToJson(this);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ const _$MqtActionEnumEnumMap = {
|
||||
MqtActionEnum.SUPERMARKET_CLASSIFY: 'SUPERMARKET_CLASSIFY',
|
||||
MqtActionEnum.PRINT: 'PRINT',
|
||||
MqtActionEnum.FACE: 'FACE',
|
||||
MqtActionEnum.TAKE_FOOD_BROADCAST: 'TAKE_FOOD_BROADCAST',
|
||||
};
|
||||
|
||||
MqttInvoiceMsg _$MqttInvoiceMsgFromJson(Map<String, dynamic> json) =>
|
||||
@@ -45,3 +46,33 @@ Map<String, dynamic> _$MqttInvoiceMsgToJson(MqttInvoiceMsg instance) =>
|
||||
'pic': instance.pic,
|
||||
'isGlobalFace': instance.isGlobalFace,
|
||||
};
|
||||
|
||||
MqttTakeFoodBroadcastMsg _$MqttTakeFoodBroadcastMsgFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
MqttTakeFoodBroadcastMsg(
|
||||
topic: json['topic'] as String,
|
||||
userId: json['userId'] as String,
|
||||
username: json['username'] as String,
|
||||
userMobile: json['userMobile'] as String,
|
||||
id: json['id'] as String,
|
||||
title: json['title'] as String,
|
||||
content: json['content'] as String,
|
||||
broadcastMaxCount: (json['broadcastMaxCount'] as num).toInt(),
|
||||
productDesc: json['productDesc'] as String,
|
||||
payPrice: (json['payPrice'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MqttTakeFoodBroadcastMsgToJson(
|
||||
MqttTakeFoodBroadcastMsg instance) =>
|
||||
<String, dynamic>{
|
||||
'topic': instance.topic,
|
||||
'userId': instance.userId,
|
||||
'username': instance.username,
|
||||
'userMobile': instance.userMobile,
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'content': instance.content,
|
||||
'broadcastMaxCount': instance.broadcastMaxCount,
|
||||
'productDesc': instance.productDesc,
|
||||
'payPrice': instance.payPrice,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:floor/floor.dart';
|
||||
|
||||
import 'package:pj_supermarket_cloud/common/index.dart';
|
||||
|
||||
// broadcast
|
||||
@dao
|
||||
abstract class BroadcastDao {
|
||||
@insert
|
||||
Future<void> save(BroadcastEntity entity);
|
||||
|
||||
@insert
|
||||
Future<void> batchSave(List<BroadcastEntity> entitys);
|
||||
|
||||
@Query("""
|
||||
SELECT * FROM broadcast
|
||||
WHERE
|
||||
isDone = 0
|
||||
AND enable = true
|
||||
AND broadcastCount < broadcastMaxCount
|
||||
AND strftime('%Y-%m-%d', createTime) = strftime('%Y-%m-%d', 'now', 'localtime')
|
||||
""")
|
||||
Future<List<BroadcastEntity>> getAll();
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import 'package:sqflite/sqflite.dart' as sqflite;
|
||||
part 'database.g.dart';
|
||||
|
||||
const entities = <Type>[
|
||||
BroadcastEntity,
|
||||
OrderEntity,
|
||||
CanteenSpuEntity,
|
||||
SupermarketSkuEntity,
|
||||
@@ -37,6 +38,7 @@ const typeConverters = <Type>[
|
||||
@Database(version: 1, entities: entities)
|
||||
@TypeConverters(typeConverters)
|
||||
abstract class AppDatabase extends FloorDatabase {
|
||||
BroadcastDao get getBroadcastDao;
|
||||
OrderDao get getOrderDao;
|
||||
CanteenSpuDao get getCanteenSpuDao;
|
||||
SupermarketSkuDao get getSupermarketSkuDao;
|
||||
|
||||
@@ -72,6 +72,8 @@ class _$AppDatabase extends AppDatabase {
|
||||
changeListener = listener ?? StreamController<String>.broadcast();
|
||||
}
|
||||
|
||||
BroadcastDao? _getBroadcastDaoInstance;
|
||||
|
||||
OrderDao? _getOrderDaoInstance;
|
||||
|
||||
CanteenSpuDao? _getCanteenSpuDaoInstance;
|
||||
@@ -115,6 +117,8 @@ class _$AppDatabase extends AppDatabase {
|
||||
await callback?.onUpgrade?.call(database, startVersion, endVersion);
|
||||
},
|
||||
onCreate: (database, version) async {
|
||||
await database.execute(
|
||||
'CREATE TABLE IF NOT EXISTS `broadcast` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `content` TEXT NOT NULL, `broadcastCount` INTEGER NOT NULL, `broadcastMaxCount` INTEGER NOT NULL, `isBroadcast` INTEGER NOT NULL, `isDone` INTEGER NOT NULL, `isDoneTime` TEXT, `serviceCode` TEXT NOT NULL, `userId` TEXT NOT NULL, `username` TEXT NOT NULL, `userMobile` TEXT NOT NULL, `productDesc` TEXT NOT NULL, `payPrice` INTEGER NOT NULL, `enable` INTEGER NOT NULL, `createTime` TEXT NOT NULL)');
|
||||
await database.execute(
|
||||
'CREATE TABLE IF NOT EXISTS `order_record` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `uniqueCode` TEXT NOT NULL, `serviceUniqueCode` TEXT NOT NULL, `sysType` TEXT NOT NULL, `userCode` TEXT NOT NULL, `username` TEXT, `hourType` TEXT, `price` INTEGER NOT NULL, `extraPrice` INTEGER NOT NULL, `payPrice` INTEGER NOT NULL, `walletId` TEXT, `orderSourceTypeId` TEXT, `type` TEXT NOT NULL, `status` TEXT NOT NULL, `payType` TEXT NOT NULL, `productDesc` TEXT, `count` INTEGER NOT NULL, `weight` REAL NOT NULL, `payTime` TEXT, `createTime` TEXT NOT NULL)');
|
||||
await database.execute(
|
||||
@@ -144,6 +148,12 @@ class _$AppDatabase extends AppDatabase {
|
||||
return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions);
|
||||
}
|
||||
|
||||
@override
|
||||
BroadcastDao get getBroadcastDao {
|
||||
return _getBroadcastDaoInstance ??=
|
||||
_$BroadcastDao(database, changeListener);
|
||||
}
|
||||
|
||||
@override
|
||||
OrderDao get getOrderDao {
|
||||
return _getOrderDaoInstance ??= _$OrderDao(database, changeListener);
|
||||
@@ -205,6 +215,71 @@ class _$AppDatabase extends AppDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
class _$BroadcastDao extends BroadcastDao {
|
||||
_$BroadcastDao(
|
||||
this.database,
|
||||
this.changeListener,
|
||||
) : _queryAdapter = QueryAdapter(database),
|
||||
_broadcastEntityInsertionAdapter = InsertionAdapter(
|
||||
database,
|
||||
'broadcast',
|
||||
(BroadcastEntity item) => <String, Object?>{
|
||||
'id': item.id,
|
||||
'content': item.content,
|
||||
'broadcastCount': item.broadcastCount,
|
||||
'broadcastMaxCount': item.broadcastMaxCount,
|
||||
'isBroadcast': _boolConverter.encode(item.isBroadcast),
|
||||
'isDone': _boolConverter.encode(item.isDone),
|
||||
'isDoneTime': _dateTimeEmptyConverter.encode(item.isDoneTime),
|
||||
'serviceCode': item.serviceCode,
|
||||
'userId': item.userId,
|
||||
'username': item.username,
|
||||
'userMobile': item.userMobile,
|
||||
'productDesc': item.productDesc,
|
||||
'payPrice': item.payPrice,
|
||||
'enable': _boolConverter.encode(item.enable),
|
||||
'createTime': _dateTimeConverter.encode(item.createTime)
|
||||
});
|
||||
|
||||
final sqflite.DatabaseExecutor database;
|
||||
|
||||
final StreamController<String> changeListener;
|
||||
|
||||
final QueryAdapter _queryAdapter;
|
||||
|
||||
final InsertionAdapter<BroadcastEntity> _broadcastEntityInsertionAdapter;
|
||||
|
||||
@override
|
||||
Future<List<BroadcastEntity>> getAll() async {
|
||||
return _queryAdapter.queryList(
|
||||
'SELECT * FROM broadcast WHERE isDone = 0 AND enable = true AND broadcastCount < broadcastMaxCount AND strftime(\'%Y-%m-%d\', createTime) = strftime(\'%Y-%m-%d\', \'now\', \'localtime\')',
|
||||
mapper: (Map<String, Object?> row) => BroadcastEntity(
|
||||
id: row['id'] as int?,
|
||||
content: row['content'] as String,
|
||||
broadcastMaxCount: row['broadcastMaxCount'] as int,
|
||||
isBroadcast: _boolConverter.decode(row['isBroadcast'] as int),
|
||||
createTime: _dateTimeConverter.decode(row['createTime'] as String),
|
||||
serviceCode: row['serviceCode'] as String,
|
||||
userId: row['userId'] as String,
|
||||
username: row['username'] as String,
|
||||
userMobile: row['userMobile'] as String,
|
||||
productDesc: row['productDesc'] as String,
|
||||
payPrice: row['payPrice'] as int));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save(BroadcastEntity entity) async {
|
||||
await _broadcastEntityInsertionAdapter.insert(
|
||||
entity, OnConflictStrategy.abort);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> batchSave(List<BroadcastEntity> entitys) async {
|
||||
await _broadcastEntityInsertionAdapter.insertList(
|
||||
entitys, OnConflictStrategy.abort);
|
||||
}
|
||||
}
|
||||
|
||||
class _$OrderDao extends OrderDao {
|
||||
_$OrderDao(
|
||||
this.database,
|
||||
|
||||
@@ -28,6 +28,27 @@ class BroadcastEntity {
|
||||
/// 完成时间
|
||||
DateTime? isDoneTime;
|
||||
|
||||
/// 服务单号
|
||||
String serviceCode;
|
||||
|
||||
/// 用户id
|
||||
String userId;
|
||||
|
||||
/// 用户姓名
|
||||
String username;
|
||||
|
||||
/// 用户手机号
|
||||
String userMobile;
|
||||
|
||||
/// 商品介绍
|
||||
String productDesc;
|
||||
|
||||
/// 支付价格
|
||||
int payPrice;
|
||||
|
||||
/// 手动开关
|
||||
bool enable = true;
|
||||
|
||||
/// 创建时间
|
||||
DateTime createTime;
|
||||
|
||||
@@ -37,6 +58,12 @@ class BroadcastEntity {
|
||||
required this.broadcastMaxCount,
|
||||
required this.isBroadcast,
|
||||
required this.createTime,
|
||||
required this.serviceCode,
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.userMobile,
|
||||
required this.productDesc,
|
||||
required this.payPrice,
|
||||
});
|
||||
|
||||
factory BroadcastEntity.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -13,12 +13,19 @@ BroadcastEntity _$BroadcastEntityFromJson(Map<String, dynamic> json) =>
|
||||
broadcastMaxCount: (json['broadcastMaxCount'] as num).toInt(),
|
||||
isBroadcast: json['isBroadcast'] as bool,
|
||||
createTime: DateTime.parse(json['createTime'] as String),
|
||||
serviceCode: json['serviceCode'] as String,
|
||||
userId: json['userId'] as String,
|
||||
username: json['username'] as String,
|
||||
userMobile: json['userMobile'] as String,
|
||||
productDesc: json['productDesc'] as String,
|
||||
payPrice: (json['payPrice'] as num).toInt(),
|
||||
)
|
||||
..broadcastCount = (json['broadcastCount'] as num).toInt()
|
||||
..isDone = json['isDone'] as bool
|
||||
..isDoneTime = json['isDoneTime'] == null
|
||||
? null
|
||||
: DateTime.parse(json['isDoneTime'] as String);
|
||||
: DateTime.parse(json['isDoneTime'] as String)
|
||||
..enable = json['enable'] as bool;
|
||||
|
||||
Map<String, dynamic> _$BroadcastEntityToJson(BroadcastEntity instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -29,5 +36,12 @@ Map<String, dynamic> _$BroadcastEntityToJson(BroadcastEntity instance) =>
|
||||
'isBroadcast': instance.isBroadcast,
|
||||
'isDone': instance.isDone,
|
||||
'isDoneTime': instance.isDoneTime?.toIso8601String(),
|
||||
'serviceCode': instance.serviceCode,
|
||||
'userId': instance.userId,
|
||||
'username': instance.username,
|
||||
'userMobile': instance.userMobile,
|
||||
'productDesc': instance.productDesc,
|
||||
'payPrice': instance.payPrice,
|
||||
'enable': instance.enable,
|
||||
'createTime': instance.createTime.toIso8601String(),
|
||||
};
|
||||
@@ -4,6 +4,7 @@ export './database/database.dart';
|
||||
|
||||
export './database/type_converter/type_converter.dart';
|
||||
|
||||
export './database/dao/broadcast_dao.dart';
|
||||
export './database/dao/classify_dao.dart';
|
||||
export './database/dao/face_dao.dart';
|
||||
export './database/dao/order_dao.dart';
|
||||
@@ -16,6 +17,7 @@ export './database/dao/supermarket_sku_dao.dart';
|
||||
export './database/dao/supermarket_spu_dao.dart';
|
||||
export './database/dao/user_dao.dart';
|
||||
|
||||
export './database/entity/broadcast_entity.dart';
|
||||
export './database/entity/classify_entity.dart';
|
||||
export './database/entity/face_entity.dart';
|
||||
export './database/entity/order_entity.dart';
|
||||
|
||||
@@ -112,6 +112,11 @@ class AppMqTool {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case MqtActionEnum.TAKE_FOOD_BROADCAST:
|
||||
mqUpdateEventBus.fire(MqtActionEnum.TAKE_FOOD_BROADCAST);
|
||||
final msg = MqttTakeFoodBroadcastMsg.fromJson(json.decode(orgStr));
|
||||
SyncTakeFoodBroadcastTool.addTask(msg);
|
||||
return;
|
||||
case MqtActionEnum.FACE:
|
||||
return;
|
||||
// final msg = MqttInvoiceMsg.fromJson(json.decode(orgStr));
|
||||
|
||||
@@ -41,4 +41,5 @@ export './sync/sync_face_tool.dart';
|
||||
export './sync/sync_order_tool.dart';
|
||||
export './sync/sync_print_tool.dart';
|
||||
export './sync/sync_product_tool.dart';
|
||||
export './sync/sync_take_food_broadcast_tool.dart';
|
||||
export './sync/sync_user_tool.dart';
|
||||
@@ -21,10 +21,14 @@ class SyncCheckTool {
|
||||
|
||||
Duration printInterval = const Duration(seconds: 60 * 10);
|
||||
|
||||
Duration broadcastInterval = const Duration(seconds: 60 * 10);
|
||||
|
||||
Timer? mqTimer;
|
||||
|
||||
Timer? printTimer;
|
||||
|
||||
Timer? broadcastTimer;
|
||||
|
||||
// **************************************************************************
|
||||
// 定时器配置 end
|
||||
// **************************************************************************
|
||||
@@ -33,6 +37,7 @@ class SyncCheckTool {
|
||||
if (_isRunning) return;
|
||||
checkMq();
|
||||
checkPrint();
|
||||
checkBroadcast();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
@@ -94,4 +99,30 @@ class SyncCheckTool {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 定时检查需不要广播
|
||||
checkBroadcast() {
|
||||
String logMsg = "定时检查需不要广播";
|
||||
final inSeconds = broadcastInterval.inSeconds;
|
||||
if (broadcastTimer != null && broadcastTimer!.isActive) {
|
||||
broadcastTimer?.cancel();
|
||||
broadcastTimer = null;
|
||||
}
|
||||
if (broadcastTimer != null) {
|
||||
broadcastTimer?.cancel();
|
||||
broadcastTimer = null;
|
||||
}
|
||||
broadcastTimer = Timer.periodic(broadcastInterval, (Timer timer) async {
|
||||
final isNetwork = AppNetworkTool.isAutoNetwork();
|
||||
if (!isNetwork) return;
|
||||
logger.i("|$_tag|$logMsg,$inSeconds秒一次,开始执行");
|
||||
try {
|
||||
await SyncTakeFoodBroadcastTool.doCheckBroadcast();
|
||||
} catch (e) {
|
||||
logger.i("|$_tag|$logMsg,$inSeconds秒一次,执行失败,error:$e");
|
||||
} finally {
|
||||
logger.i("|$_tag|$logMsg,$inSeconds秒一次,执行完成");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:common_utils/common_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:pj_supermarket_cloud/common/index.dart';
|
||||
|
||||
abstract class SyncTakeFoodBroadcastTool {
|
||||
static const _tag = "sync订餐广播工具";
|
||||
|
||||
static bool _isBroadcast = false;
|
||||
|
||||
static Future<void> addTask(MqttTakeFoodBroadcastMsg msg) async {
|
||||
final appC = Get.find<AppControl>();
|
||||
final broadcastDao = appC.dataBase.getBroadcastDao;
|
||||
final saveObj = BroadcastEntity(
|
||||
content: msg.content,
|
||||
broadcastMaxCount: msg.broadcastMaxCount,
|
||||
isBroadcast: true,
|
||||
productDesc: msg.productDesc,
|
||||
serviceCode: msg.id,
|
||||
userId: msg.userId,
|
||||
username: msg.username,
|
||||
userMobile: msg.userMobile,
|
||||
payPrice: msg.payPrice,
|
||||
createTime: DateTime.now(),
|
||||
);
|
||||
await broadcastDao.save(saveObj);
|
||||
logger.i("|$_tag|添加到打印记录到本地数据库,json:|${saveObj.toJson()}|");
|
||||
await SyncTakeFoodBroadcastTool.doCheckBroadcast();
|
||||
}
|
||||
|
||||
// 检查广播
|
||||
static Future<void> doCheckBroadcast() async {
|
||||
if (_isBroadcast) {
|
||||
logger.i("|$_tag|检查打印,正在广播中");
|
||||
return;
|
||||
}
|
||||
_isBroadcast = true;
|
||||
final appC = Get.find<AppControl>();
|
||||
final broadcastDao = appC.dataBase.getBroadcastDao;
|
||||
try {
|
||||
final list = await broadcastDao.getAll();
|
||||
if (ObjectUtil.isEmptyList(list)) {
|
||||
_isBroadcast = false;
|
||||
logger.i("|$_tag|检查广播,没有需要广播的订单信息");
|
||||
return;
|
||||
}
|
||||
// 将列表分组,每组5个元素
|
||||
final int groupSize = 5;
|
||||
final int totalGroups = (list.length / groupSize).ceil();
|
||||
for (int i = 0; i < totalGroups; i++) {
|
||||
// 计算当前组的起始和结束索引
|
||||
final int start = i * groupSize;
|
||||
final int end = (start + groupSize) < list.length
|
||||
? (start + groupSize)
|
||||
: list.length;
|
||||
// 获取当前组的元素
|
||||
final currentGroup = list.sublist(start, end);
|
||||
String msg = "请手机尾号,";
|
||||
for (final v in currentGroup) {
|
||||
msg += "${v.content},";
|
||||
}
|
||||
msg = msg.substring(0, msg.length - 1);
|
||||
msg += "的用户,请尽快取餐";
|
||||
// 播报当前组
|
||||
await TtsManage.speak(msg);
|
||||
// 如果不是最后一组,等待一段时间再播报下一组
|
||||
if (i < totalGroups - 1) {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
}
|
||||
}
|
||||
} catch (e, stack) {
|
||||
logger.e("|$_tag|检查广播,广播错误,error:|$e|stack:|$stack|");
|
||||
} finally {
|
||||
_isBroadcast = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user