feat:本地商品bug

This commit is contained in:
dzc committed 2025-03-20 10:42:31 +08:00
1 parent 32341ef253
commit 1f50f737b1
11 files changed
+152 -92

No files matched your search

@@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:floor/floor.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
@@ -6,7 +7,7 @@ part 'supermarket_spu_entity.g.dart';
@JsonSerializable()
@Entity(tableName: "supermarket_spu")
class SupermarketSpuEntity {
class SupermarketSpuEntity extends Equatable {
/// 自增ID
@PrimaryKey(autoGenerate: true)
int? id;
@@ -95,4 +96,22 @@ class SupermarketSpuEntity {
_$SupermarketSpuEntityFromJson(json);
Map<String, dynamic> toJson() => _$SupermarketSpuEntityToJson(this);
@override
List<Object?> get props => [
serviceUniqueCode,
classifyServiceUniqueCode,
productCode,
productName,
productNature,
barCode,
packageNum,
unit,
spec,
picUrl,
stock,
purchasePrice,
retailPrice,
promotionPrice,
];
}
@@ -17,12 +17,16 @@ class BarcodeScanManage {
static BarcodeScanManage get instance => _instance;
bool _isListening = false;
String _buffer = '';
StringBuffer _buffer = StringBuffer();
Timer? _timer;
final StreamController<String> _scanController =
StreamController<String>.broadcast();
Stream<String> get scanStream => _scanController.stream;
// 二维码扫码结果
final _qrcodeScanController = StreamController<String>.broadcast();
Stream<String> get qrcodeScanStream => _qrcodeScanController.stream;
// 扫码枪扫码结果
final _gunScanController = StreamController<String>.broadcast();
Stream<String> get gunScanStream => _gunScanController.stream;
// 开始监听USB读头输入
void startListening() {
@@ -30,44 +34,56 @@ class BarcodeScanManage {
_isListening = true;
// 确保没有焦点的控件
FocusManager.instance.primaryFocus?.unfocus();
RawKeyboard.instance.addListener(_handleKeyEvent);
HardwareKeyboard.instance.addHandler(_handleKeyEvent);
logger.i("|$_tag|开始监听USB读头输入");
}
// 停止监听
void stopListening() {
if (!_isListening) return;
RawKeyboard.instance.removeListener(_handleKeyEvent);
HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
_timer?.cancel();
_isListening = false;
logger.i("|$_tag|停止监听USB读头输入");
}
// 处理键盘事件
void _handleKeyEvent(RawKeyEvent event) {
if (event is! RawKeyDownEvent) return;
// 阻止键盘弹出
FocusManager.instance.primaryFocus?.unfocus();
// 重置定时器
_timer?.cancel();
// 处理回车键,表示扫码结束
if (event.logicalKey.keyId == 0x10000000d) {
if (_buffer.isNotEmpty) {
logger.i("|$_tag|扫码结果: $_buffer");
_scanController.add(_buffer);
_buffer = '';
bool _handleKeyEvent(KeyEvent event) {
if (event is! KeyDownEvent) return false;
try {
FocusManager.instance.primaryFocus?.unfocus();
_timer?.cancel();
// 处理回车键,表示扫码结束
if (event.logicalKey.keyId == 0x10000000d) {
if (_buffer.isNotEmpty) {
final result = _buffer.toString();
logger.i("|$_tag|扫码结果: $result");
if (result.length >= 100) {
_qrcodeScanController.add(result);
} else {
_gunScanController.add(result);
}
_buffer.clear();
}
return true;
}
return;
}
// 获取字符
final character = _getCharacter(event);
if (character != null) {
_buffer += character;
// 设置超时定时器,如果超过一定时间没有新输入,则清空缓冲区
_timer = Timer(Duration(milliseconds: 100), () {
_buffer = '';
});
final character = _getCharacter(event);
if (character != null) {
_buffer.write(character);
// 设置超时定时器,如果超过一定时间没有新输入,则清空缓冲区
_timer = Timer(Duration(milliseconds: 100), () {
if (_buffer.isNotEmpty) {
logger.d("|$_tag|扫码超时,清空缓冲区");
_buffer.clear();
}
});
}
return true;
} catch (e, stack) {
logger.e("|$_tag|处理键盘事件错误error|$e|stack|$stack|");
_buffer.clear();
_timer?.cancel();
return false;
}
}
@@ -82,31 +98,28 @@ class BarcodeScanManage {
}
// 从键盘事件中获取字符
String? _getCharacter(RawKeyEvent event) {
String? _getCharacter(KeyEvent event) {
// 处理数字键
if (event.logicalKey.keyId >= LogicalKeyboardKey.digit0.keyId &&
event.logicalKey.keyId <= LogicalKeyboardKey.digit9.keyId) {
return String.fromCharCode(
event.logicalKey.keyId - LogicalKeyboardKey.digit0.keyId + 48);
}
// 处理小写字母键
// 处理小写字母键
if (event.logicalKey.keyId >= LogicalKeyboardKey.keyA.keyId &&
event.logicalKey.keyId <= LogicalKeyboardKey.keyZ.keyId) {
// 检查是否按下了Shift键,决定返回大写还是小写字母
final isShiftPressed = event.isShiftPressed;
final isShiftPressed = HardwareKeyboard.instance.isShiftPressed;
final charCode = event.logicalKey.keyId -
LogicalKeyboardKey.keyA.keyId +
(isShiftPressed ? 65 : 97);
return String.fromCharCode(charCode);
}
// 处理其他常见字符
switch (event.logicalKey) {
case LogicalKeyboardKey.minus:
return event.isShiftPressed ? '_' : '-';
return HardwareKeyboard.instance.isShiftPressed ? '_' : '-';
case LogicalKeyboardKey.equal:
return event.isShiftPressed ? '+' : '=';
return HardwareKeyboard.instance.isShiftPressed ? '+' : '=';
case LogicalKeyboardKey.bracketLeft:
return '[';
case LogicalKeyboardKey.bracketRight:
@@ -122,7 +135,7 @@ class BarcodeScanManage {
case LogicalKeyboardKey.period:
return '.';
case LogicalKeyboardKey.slash:
return event.isShiftPressed ? '?' : '/';
return HardwareKeyboard.instance.isShiftPressed ? '?' : '/';
case LogicalKeyboardKey.add:
return '+';
case LogicalKeyboardKey.underscore:
@@ -177,6 +190,15 @@ class BarcodeScanManage {
// 释放资源
void dispose() {
stopListening();
_scanController.close();
_qrcodeScanController.close();
_gunScanController.close();
}
void disposeQrCodeScan() {
_qrcodeScanController.close();
}
void disposeGunScan() {
_gunScanController.close();
}
}
+30 -20
View File
@@ -163,21 +163,34 @@ abstract class SyncProductTool {
await _batchUpdateCaneenProduct(list: canteenList);
} else if (sysType == SysType.SUPERMARKET) {
// 超市
final app = Get.find<AppControl>();
final dataBase = app.dataBase;
final supermarketSpuDao = dataBase.getSupermarketSpuDao;
final supermarketSkuDao = dataBase.getSupermarketSkuDao;
final supermarketCodeDao = dataBase.getSupermarketCodeDao;
int currentPage = 1;
Set<String> serviceCodes = {};
while (true) {
final pa = {
"current": currentPage,
"size": 50,
};
final supermarket = await ProductApi.supermarketSpu(
pa,
final pa = {"current": currentPage, "size": 50};
final supermarket = await ProductApi.supermarketSpu(pa);
if (supermarket == null) break;
if (ObjectUtil.isEmptyList(supermarket.records)) break;
final ids = await _batchUpdateSupermarketProduct(
list: supermarket.records!,
);
if (supermarket == null ||
ObjectUtil.isEmptyList(supermarket.records)) {
break;
}
currentPage += 1;
await _batchUpdateSupermarketProduct(list: supermarket.records!);
serviceCodes.addAll(ids);
}
// 处理删除
final productList = await supermarketSpuDao.getAll(
"-1",
);
final dbCodes = productList.map((e) => e.serviceUniqueCode).toSet();
final deleteCodes = dbCodes.difference(serviceCodes).toList();
if (ObjectUtil.isNotEmpty(deleteCodes)) {
await supermarketSpuDao.delByIds(deleteCodes);
await supermarketSkuDao.delBySpuIds(deleteCodes);
await supermarketCodeDao.delBySpuIds(deleteCodes);
}
}
// 更新同步时间
@@ -299,7 +312,7 @@ abstract class SyncProductTool {
// 同步食堂商品 spu start
// **************************************************************************
static Future<void> _batchUpdateSupermarketProduct({
static Future<List<String>> _batchUpdateSupermarketProduct({
required List<ServiceSupermarketSpu> list,
}) async {
final app = Get.find<AppControl>();
@@ -314,13 +327,12 @@ abstract class SyncProductTool {
isUpdate: false,
);
await _batchAddOrUpdateSupermarketSpuAndCode(list);
return;
return list.map((e) => e.id.toString()).toSet().toList();
}
final apiServiceCodes = list.map((e) => e.id.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 _batchAddOrUpdateSupermarketProduct(
@@ -328,9 +340,6 @@ abstract class SyncProductTool {
isUpdate: false,
);
}
if (ObjectUtil.isNotEmpty(deleteCodes)) {
await supermarketSpuDao.delByIds(deleteCodes);
}
if (ObjectUtil.isNotEmpty(updateCodes)) {
await _batchAddOrUpdateSupermarketProduct(
list: _filterCaneenSupermarketByCodes(list, updateCodes),
@@ -338,6 +347,7 @@ abstract class SyncProductTool {
);
}
await _batchAddOrUpdateSupermarketSpuAndCode(list);
return apiServiceCodes.toList();
}
static Future<void> _batchAddOrUpdateSupermarketProduct({
@@ -354,13 +364,13 @@ abstract class SyncProductTool {
serviceUniqueCode: v.id ?? "-1",
classifyServiceUniqueCode: v.categoryId ?? "-1",
productCode: v.productCode ?? "-1",
productName: v.productCode ?? "",
productName: v.productName ?? "",
productNature: ProductNature.values.fromCode(v.productNature) ??
ProductNature.DEFAULT,
barCode: "",
packageNum: 0,
unit: v.productCode ?? "",
spec: v.productCode ?? "",
unit: v.unit ?? "",
spec: v.spec ?? "",
picUrl: v.picUrl ?? "",
stock: v.stock ?? 0,
salesCount:
+2 -2
View File
@@ -10,9 +10,9 @@ class Constants {
static const pwdEncKey = 'pigxpigxpigxpigx';
/// 服务 url
static const apiUrl = 'https://stgl.pj.gov.cn/api';
// static const apiUrl = 'https://stgl.pj.gov.cn/api';
// static const apiUrl = 'http://192.168.101.64:9999';
static const apiUrl = 'http://192.168.101.64:9999';
/// 静态资源 url
static const ossUrl = apiUrl;
@@ -7,9 +7,10 @@ import 'package:pj_supermarket_cloud/common/index.dart';
Widget orderSupermarketSpuCard(
BuildContext context, {
required SupermarketSpuEntity supermarketSpu,
bool visibleProductImg = false,
}) {
final theme = FTheme.of(context);
final textStyle = theme.typography.sm.copyWith(
final textStyle = theme.typography.xs.copyWith(
color: theme.colorScheme.primary,
);
return FCard(
@@ -19,17 +20,21 @@ Widget orderSupermarketSpuCard(
),
),
image: appImg(supermarketSpu.picUrl, width: double.infinity, height: 100.h)
.center(),
.center()
.offstage(offstage: !visibleProductImg),
subtitle: <Widget>[
Text(supermarketSpu.productName, style: theme.typography.xl),
Text(
supermarketSpu.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.typography.base,
),
SizedBox(height: 5.h),
FBadge(
style: FBadgeStyle.secondary,
label: Text(
supermarketSpu.productNature.title,
style: textStyle,
),
label: Text(supermarketSpu.productNature.title, style: textStyle),
),
SizedBox(height: 5.h),
<Widget>[
Text("零售价:", style: textStyle),
appPrice(context, supermarketSpu.retailPrice),
@@ -42,16 +47,19 @@ Widget orderSupermarketSpuCard(
Text("货号:", style: textStyle),
Text(supermarketSpu.productCode, style: textStyle),
].toRow(mainAxisAlignment: MainAxisAlignment.center),
<Widget>[
Text("辅助条码:", style: textStyle),
Text(supermarketSpu.barCode ?? "", style: textStyle),
].toRow(mainAxisAlignment: MainAxisAlignment.center),
<Widget>[
Text("包装单位:", style: textStyle),
Text("${supermarketSpu.packageNum}", style: textStyle),
Text("/"),
Text(supermarketSpu.unit, style: textStyle),
].toRow(mainAxisAlignment: MainAxisAlignment.center),
<Widget>[
Text("条码:", style: textStyle),
Text(
supermarketSpu.barCode ?? "",
style: textStyle,
),
].toRow(mainAxisAlignment: MainAxisAlignment.center),
]
.toColumn(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -105,7 +105,9 @@ class SelectPayTypeDialogController extends GetxController {
globalQrCode = null;
_faceCallbackLock = false;
// 设备
BarcodeScanManage.instance.dispose();
if (appSetting.sysType == SysType.CANTEEN) {
BarcodeScanManage.instance.dispose();
}
app.serialCardManage.close();
// 取消人脸监听
@@ -171,8 +173,11 @@ class SelectPayTypeDialogController extends GetxController {
return;
}
TtsManage.speak("请出示二维码");
isRecognition.value = true;
onDiscernUser?.call(payUser.value);
// 先关闭,超市有扫码枪
BarcodeScanManage.instance.startListening();
BarcodeScanManage.instance.scanStream.listen((event) async {
BarcodeScanManage.instance.qrcodeScanStream.listen((event) async {
final serviceCode = await BarcodeScanManage.instance.getCodeInfo(event);
if (serviceCode == null) return;
if (serviceCode.exceedTime != -1) {
@@ -181,26 +186,26 @@ class SelectPayTypeDialogController extends GetxController {
).millisecondsSinceEpoch;
int nowTime = DateTime.now().millisecondsSinceEpoch;
if (exceedTime <= nowTime) {
Toast.error(msg: "识别错误,二维码已过期");
TtsManage.speak("二维码已过期");
return;
}
}
globalQrCode = serviceCode;
if (!_validateQrCodeType(orderPayType.value!)) {
if (!_validateQrCodeType(serviceCode.payType!)) {
logger.e("|$_tag|二维码识别,无效的二维码json:|${jsonEncode(serviceCode)}|");
Toast.error(msg: "识别错误,无效的二维码");
TtsManage.speak("无效的二维码");
return;
}
globalQrCode = serviceCode;
orderPayType.value = serviceCode.payType;
logger.i("|$_tag|二维码识别,识别到的二维码json:|${jsonEncode(serviceCode)}|");
final isNetwork = AppNetworkTool.isAutoNetwork();
final appSetting = await AppSettingTool.getSysSetting();
if (isNetwork) {
final pa = {
"id": serviceCode.userId,
"type": appSetting.sysType.name,
};
final data = await HardApi.getUserInfoById(pa);
if (data != null) throw Error();
if (data == null) throw Exception("用户不存在");
payUser.value = data;
if (payUser.value != null) {
await SyncUserTool.storageUser(user: payUser.value!);
@@ -215,13 +220,9 @@ class SelectPayTypeDialogController extends GetxController {
}
}
isRecognition.value = false;
BarcodeScanManage.instance.dispose();
Toast.success(msg: "识别二维码成功");
TtsManage.speak("识别二维码成功");
});
isRecognition.value = true;
onDiscernUser?.call(payUser.value);
} catch (e) {
TtsManage.speak("识别二维码错误");
logger.e("|$_tag|二维码识别,失败error:|$e|");
}
}
@@ -17,11 +17,11 @@ Future<void> _bindServiceDialog(BuildContext context, SysSetting sys) async {
}
if (AppSysTool.isDev()) {
// 正式环境-超市
uniqueCodeC.text = "1894303392833716226";
// uniqueCodeC.text = "1894303392833716226";
// 正式环境-测试
// uniqueCodeC.text = "1896157295523049473";
// 测试环境-测试设备
// uniqueCodeC.text = "1877592554489143298";
uniqueCodeC.text = "1877592554489143298";
}
await showAdaptiveDialog(
context: context,
@@ -274,7 +274,7 @@ Widget _buildSupermarketList(BuildContext context) {
crossAxisCount: 6,
mainAxisSpacing: 10.w,
crossAxisSpacing: 10.w,
childAspectRatio: 0.7,
childAspectRatio: 0.9,
),
itemCount: productC.supermarketSpuList.length,
itemBuilder: (BuildContext context, int index) {
+1 -1
View File
@@ -297,7 +297,7 @@ class SupermarketOrderController extends GetxController with OrderMixin {
Future<void> _listenBarCodeCallback() async {
BarcodeScanManage.instance.startListening();
BarcodeScanManage.instance.scanStream.listen((event) async {
BarcodeScanManage.instance.gunScanStream.listen((event) async {
if (ObjectUtil.isEmpty(event)) return;
if (event.length >= 30) return;
// 根据条码查询商品
+1 -1
View File
@@ -141,7 +141,7 @@ class SupermarketOrderPage extends GetView<SupermarketOrderController> {
double maxHeight = constraints.maxHeight;
double maxWidth = constraints.maxWidth;
double itemWidth = (maxWidth - spacing * 4) / 5;
double itemHeight = (maxHeight - (runSpacing + 30) * 2) / 2;
double itemHeight = (maxHeight - (runSpacing + 130) * 2) / 2;
return Obx(
() => ListView.builder(
padding: EdgeInsets.zero,
+1 -1
View File
@@ -96,7 +96,7 @@ class TakeFoodController extends GetxController {
try {
// 监听二维码测试
BarcodeScanManage.instance.startListening();
BarcodeScanManage.instance.scanStream.listen((event) async {
BarcodeScanManage.instance.qrcodeScanStream.listen((event) async {
final serviceCode = await BarcodeScanManage.instance.getCodeInfo(event);
if (serviceCode == null) return;
if (serviceCode.payType != OrderPayType.RESERVE_QR_CODE) {