重构子零件码验证逻辑,提升兼容性与健壮性

将子零件码(摄像头码)验证规则全部下沉至 ScanService 服务层,支持多种日期格式(ddMMyy、ddMMyyyy、yyMMdd)智能提取与判定,统一以日期大于等于 2025-11-01 为合格标准。视图层仅负责调用服务、异常处理和流程控制,提升了代码可维护性和扫码兼容性。完善了异常处理和日志记录,优化用户体验。
This commit is contained in:
2025-12-16 10:42:04 +08:00
parent bb73b0f9c5
commit 1c7b192270
2 changed files with 133 additions and 11 deletions

View File

@@ -30,7 +30,89 @@ namespace LineTraceabilitySystem.Services
public bool ValidateSubPartCode(string code) public bool ValidateSubPartCode(string code)
{ {
return true; // 将子零件码的判定规则集中在此处,视图层不再包含具体规则。
// 新规则:
// 1. 不限制长度
// 2. 在二维码中提取日期信息,日期小于 2025-11-01 判定为不合格,>= 判定为合格
// 3. 日期可能以 6 位 (ddMMyy) 或 8 位 (ddMMyyyy) 等形式存在,解析应具备较高兼容性,避免简单的固定位置截取
if (string.IsNullOrWhiteSpace(code))
{
return false;
}
code = code.Trim();
// 优先尝试在整个字符串中查找 6 或 8 位连续数字序列作为候选日期字段
// 使用正则以提高兼容性(避免基于固定下标截取)
try
{
var matches = System.Text.RegularExpressions.Regex.Matches(code, "\\d{6,8}");
var threshold = new DateTime(2025, 11, 1);
foreach (System.Text.RegularExpressions.Match m in matches)
{
var seq = m.Value;
// 优先尝试 ddMMyyyy (8 位)
if (seq.Length == 8)
{
if (int.TryParse(seq.Substring(0, 2), out var dd)
&& int.TryParse(seq.Substring(2, 2), out var MM)
&& int.TryParse(seq.Substring(4, 4), out var yyyy))
{
if (IsValidDate(yyyy, MM, dd, threshold))
return true;
}
}
// 兼容 6 位形式,通常为 ddMMyy261125 表示 26-11-25 -> 2025-11-26
if (seq.Length == 6)
{
if (int.TryParse(seq.Substring(0, 2), out var dd)
&& int.TryParse(seq.Substring(2, 2), out var MM)
&& int.TryParse(seq.Substring(4, 2), out var yy))
{
var yyyy = 2000 + yy; // 以 2000 年为基准
if (IsValidDate(yyyy, MM, dd, threshold))
return true;
}
// 兼容性扩展:如果 6 位不能按 ddMMyy 解析,通过 yyMMdd 尝试解析
if (int.TryParse(seq.Substring(0, 2), out var alt1)
&& int.TryParse(seq.Substring(2, 2), out var alt2)
&& int.TryParse(seq.Substring(4, 2), out var alt3))
{
// 尝试 yyMMdd -> year = 2000 + alt1, month=alt2, day=alt3
var yyyy = 2000 + alt1;
if (IsValidDate(yyyy, alt2, alt3, threshold))
return true;
}
}
}
}
catch
{
// 解析异常视为不合格
return false;
}
// 未找到满足条件的日期序列,判定为不合格
return false;
}
/// <summary>
/// 判断指定年月日是否合法并且大于等于阈值日期
/// </summary>
private static bool IsValidDate(int year, int month, int day, DateTime threshold)
{
try
{
var dt = new DateTime(year, month, day);
return dt >= threshold;
}
catch
{
return false;
}
} }
public bool ValidateTraceabilityCode(string code) public bool ValidateTraceabilityCode(string code)

View File

@@ -602,46 +602,86 @@ namespace LineTraceabilitySystem.ViewModels
} }
/// <summary> /// <summary>
/// 处理摄像头码扫描事件 /// 处理摄像头码扫描事件(异步)。
/// 验证规则已全部封装到 `IScanService.ValidateSubPartCode` 中,视图层只负责调用、展示状态与流程控制。
/// </summary> /// </summary>
/// <param name="code">扫描到的摄像头码</param> /// <param name="code">扫描到的摄像头码</param>
private async Task OnSubPartCodeScannedAsync(string code) private async Task OnSubPartCodeScannedAsync(string code)
{ {
// 当前子件名称(供后续上传使用)
string partCodeScanName = _scanList[CurrentStep]; string partCodeScanName = _scanList[CurrentStep];
// 记录扫码结果到视图模型以便绑定更新
SubPartScanCode = code; SubPartScanCode = code;
// 验证摄像头码逻辑 - 注意ITraceMainService中可能没有VerifySubPartScanCode方法
// 以下代码需要根据实际ITraceMainService接口定义进行调整 bool isSubPartScanCodeValid;
bool isSubPartScanCodeValid = code.Contains("2F1980546#")&& code.Length==31 && _scanService.ValidateSubPartCode(code); try
{
// 将所有判定逻辑放到扫描服务中,此处仅调用并捕获可能的异常
isSubPartScanCodeValid = _scanService.ValidateSubPartCode(code);
}
catch (Exception ex)
{
// 服务验证出现异常,记录并提示,视为验证失败
AddToOperationHistory($"验证子零件码异常: {ex.Message}");
_alertService.ShowError("验证子零件码时发生错误,请检查日志");
isSubPartScanCodeValid = false;
}
// 更新状态显示
SubPartScanCodeStatus = isSubPartScanCodeValid ? "OK" : "NG"; SubPartScanCodeStatus = isSubPartScanCodeValid ? "OK" : "NG";
SubPartScanCodeStatusColor = isSubPartScanCodeValid ? Brushes.Green : Brushes.Red; SubPartScanCodeStatusColor = isSubPartScanCodeValid ? Brushes.Green : Brushes.Red;
if (!isSubPartScanCodeValid) if (!isSubPartScanCodeValid)
{ {
// 验证不通过更新UI状态并记录日志结束本次扫描流程
ScanResult = "NG"; ScanResult = "NG";
IsScanOk = false; IsScanOk = false;
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
_alertService.ShowWarning("不是有效的码"); _alertService.ShowWarning("不是有效的码");
return; return;
} }
// 验证通过
ScanResult = "OK";
IsScanOk = true;
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}"); AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
// 进入下一步
CurrentStep += 1; CurrentStep += 1;
if (CurrentStep == _scanList.Count) // 判断是否已完成所有待扫项。如果完成触发PLC相关写入并进入上传步骤。
if (CurrentStep >= _scanList.Count)
{ {
Debug.WriteLine($"追溯数据上传服务器"); AddToOperationHistory("追溯数据准备上传(所有子件已扫)");
AddToOperationHistory($"追溯数据上传服务器"); try
TagServer tagServer = HostManager.GetTagServerByIp(PLCIP);
if (CheckTagNameValid(tagServer, ScrewGunEnable))
{ {
tagServer.WriteIntSingle(tagCommon.TagAddress, 1); TagServer tagServer = HostManager.GetTagServerByIp(PLCIP);
if (CheckTagNameValid(tagServer, ScrewGunEnable))
{
// 通知PLC启用拧紧枪或触发拍照
tagServer.WriteIntSingle(tagCommon.TagAddress, 1);
}
} }
catch (Exception ex)
{
AddToOperationHistory($"向PLC写入触发位失败: {ex.Message}");
}
// 使用特殊值表示上传阶段
CurrentStep = 1000; CurrentStep = 1000;
} }
// 上传阶段
if (CurrentStep == 1000) if (CurrentStep == 1000)
{ {
await OnUploadScanned(SubPartScanCode, partCodeScanName); await OnUploadScanned(SubPartScanCode, partCodeScanName);
// 重置流程,准备下一个追溯件
Initialize(); Initialize();
} }
else else
{ {
// 更新提示文本,等待下一个扫码
CurrentStepText = $"请扫{_scanList[CurrentStep]}码"; CurrentStepText = $"请扫{_scanList[CurrentStep]}码";
} }
} }