feat:新版本读卡适配梦卡诺

This commit is contained in:
dzc committed 2026-10-26 14:33:55 +08:00
1 parent 82b73a8a03
commit 4c76840ae1
10 files changed
+285 -40

No files matched your search

+16 -5
View File
@@ -639,6 +639,20 @@ abstract class AppSettingTool {
sysJson[_serialCardConfigKey] = config.toJson();
});
/// 仅保存本机可编辑项;保留并发同步得到的最新密码 B,避免页面旧快照覆盖认证密钥。
static Future<void> setSerialCardLocalConfig(SerialCardConfig config) =>
_patchSerialCardConfig(config.toJson()..remove('keyBHex'));
/// 在已有设置写入队列中合并读写卡配置,位置编辑与密钥同步互不覆盖。
static Future<void> _patchSerialCardConfig(Map<String, dynamic> patch) =>
_updateSysJson((sysJson) {
final source = sysJson[_serialCardConfigKey];
sysJson[_serialCardConfigKey] = <String, dynamic>{
if (source is Map) ...Map<String, dynamic>.from(source),
...patch,
};
});
/// 查询方式顺序和默认项属于页面交互配置,继续保存在 sysJson 中,不增加数据库字段。
static Future<ReservationQueryPagePreferences>
getReservationQueryPagePreferences() async {
@@ -948,14 +962,11 @@ abstract class AppSettingTool {
// 商户饭卡密码 B 单独保存在读写卡配置里;服务端未返回或值非法时不覆盖本机缓存,
// 保证离线场景仍能用上一次同步成功的密钥。
final cardKey = data.cardKey?.trim();
if (cardKey != null &&
RegExp(r'^[0-9a-fA-F]{12}$').hasMatch(cardKey)) {
if (cardKey != null && RegExp(r'^[0-9a-fA-F]{12}$').hasMatch(cardKey)) {
final cardConfig = await getSerialCardConfig();
final normalizedKey = cardKey.toUpperCase();
if (cardConfig.keyBHex != normalizedKey) {
await setSerialCardConfig(
cardConfig.copyWith(keyBHex: normalizedKey),
);
await _patchSerialCardConfig({'keyBHex': normalizedKey});
logger.i('同步商户饭卡密码B成功');
}
}
+12 -11
View File
@@ -18,7 +18,7 @@ enum _SerialCardStep {
/// DK25-ST 串口读写管理。
///
/// 串口帧可能被拆包或粘包,因此先按协议长度字段解帧,再根据卡类型执行 UID 或 M1 块操作。
/// 新规范:M1 业务卡号固定读写扇区 1 的块 5,认证固定密码 B(mch_manage.card_key 同步缓存),
/// M1 业务卡号按本机配置读写(默认扇区 1 块 5),认证固定密码 B(mch_manage.card_key 同步缓存),
/// 业务块读不出时回退返回 UID;密钥仅用于扇区访问认证,不适用于其他卡型。
class SerialCardManage extends SerialBase {
/// 读卡器保持响应但在单轮等待期内没有读取到卡片时返回的稳定提示,用于业务层区分设备故障。
@@ -135,7 +135,7 @@ class SerialCardManage extends SerialBase {
));
}
/// 读卡:M1 用密码 B 读扇区 1 业务块,读不出或其他卡型返回完整 UID。
/// 读卡:M1 用密码 B 读取本机选定业务块,读不出或其他卡型返回完整 UID。
void sendRead() {
unawaited(_startOperation(command: SerialCardCmdEnum.read));
}
@@ -164,7 +164,7 @@ class SerialCardManage extends SerialBase {
_config = await AppSettingTool.getSerialCardConfig();
// 只记录非敏感协议参数,严禁记录密钥内容。
logger.i(logMsg(
'加载读写卡配置block:|${SerialCardConfig.mifareBusinessBlockAddress}|keyType:|B|',
'加载读写卡配置block:|${_config.businessBlockAddress}|keyType:|B|',
));
// 用户可能在读取本地设置期间关闭弹窗或重新发起操作,旧异步任务不得重新占用串口。
if (operationEpoch != _operationEpoch ||
@@ -278,8 +278,7 @@ class SerialCardManage extends SerialBase {
case _SerialCardStep.readBlock:
if (frame.command != SerialReadCardTool.mifareReadBlockCommand ||
frame.payload.length != 17 ||
frame.payload.first !=
SerialCardConfig.mifareBusinessBlockAddress) {
frame.payload.first != _config.businessBlockAddress) {
return;
}
_finishMifareBlockRead(frame.payload.sublist(1));
@@ -388,7 +387,7 @@ class SerialCardManage extends SerialBase {
return;
}
// 读卡固定行为:M1 先读扇区 1 业务块,其余卡型直接读取 UID。
// M1 先读本轮加载的业务块,操作中不切换位置;其余卡型直接读取 UID。
if (_cardType == SerialCardType.mifare) {
_requestActiveMifareKey();
} else {
@@ -439,7 +438,7 @@ class SerialCardManage extends SerialBase {
);
}
/// 把服务端同步的商户饭卡密码作为 KEY B 写入模块,供扇区 1 业务块认证使用。
/// 把服务端同步的商户饭卡密码作为 KEY B 写入模块,供当前业务块所在扇区认证使用。
void _requestActiveMifareKey() {
final keyBytes = SerialTool.stringToBytes(_config.keyBHex);
_setRequest(
@@ -463,16 +462,18 @@ class SerialCardManage extends SerialBase {
);
}
/// 请求本轮配置指定的业务块,响应也必须匹配同一地址。
void _requestMifareBlockRead() {
_setRequest(
step: _SerialCardStep.readBlock,
request: SerialReadCardTool.readMifareBlockRequest(
SerialCardConfig.mifareBusinessBlockAddress,
_config.businessBlockAddress,
),
label: '读取 M1 块 ${SerialCardConfig.mifareBusinessBlockAddress}',
label: '读取 M1 块 ${_config.businessBlockAddress}',
);
}
/// 仅向本轮配置指定的业务数据块写入,继续使用既有单块文本编码。
void _requestMifareBlockWrite() {
final blockData = SerialReadCardTool.encodeMifareBlockData(
_pendingWriteCardNumber ?? '',
@@ -485,10 +486,10 @@ class SerialCardManage extends SerialBase {
_setRequest(
step: _SerialCardStep.writeBlock,
request: SerialReadCardTool.writeMifareBlockRequest(
blockAddress: SerialCardConfig.mifareBusinessBlockAddress,
blockAddress: _config.businessBlockAddress,
blockData: blockData,
),
label: '写入 M1 块 ${SerialCardConfig.mifareBusinessBlockAddress}',
label: '写入 M1 块 ${_config.businessBlockAddress}',
);
}
@@ -155,9 +155,7 @@ abstract class SerialReadCardTool {
/// 第 0 块保存 UID/厂商信息,扇区尾块保存密钥;业务卡号只能使用其余数据块。
static bool isMifareBusinessDataBlock(int blockAddress) {
return blockAddress > 0 &&
blockAddress <= 255 &&
!isMifareTrailerBlock(blockAddress);
return SerialCardConfig.isValidBusinessBlockAddress(blockAddress);
}
/// 返回 S70 最大容量范围内全部 215 个安全数据块;扫描诊断按该列表完整发起读块请求。