From 9353b439a7c3868a0b3e8b354d2808a53cc68353 Mon Sep 17 00:00:00 2001 From: zhaoxing <2558691827@qq.com> Date: Sat, 14 Nov 2026 16:17:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B7=B2=E4=B8=8A=E6=8A=A5=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E7=9A=84=20Token=20=E4=B8=8D=E5=86=8D=E8=AE=A1?= =?UTF-8?q?=E5=85=A5=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 异常判定排除 status=abnormal 的 Token:库存状态已明确标识失效,不重复标记 - 与已发货排除共用 rockAnomalyTokenScopeSQL,筛选/统计/列表标记口径一致 - 新增已上报失效 Token 排除的单元测试 --- .../internal/service/rock_anomaly_marker.go | 11 ++-- .../service/rock_anomaly_marker_test.go | 63 +++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/backend/internal/service/rock_anomaly_marker.go b/backend/internal/service/rock_anomaly_marker.go index 454d5b4..6cc150c 100644 --- a/backend/internal/service/rock_anomaly_marker.go +++ b/backend/internal/service/rock_anomaly_marker.go @@ -12,8 +12,10 @@ import ( // 异常判定口径(与产品确认一致): // 1. 状态流转历史中出现过 mismatch / abnormal(绑定错误、上游失效); // 2. 状态流转原因或备注中出现失败类关键字(兑换失败、刷新失败、卡密不存在、超时等); -// 3. 已最终发货的 Token(status=redeemed 或 redeemed_at 非空)不再算异常: -// 发货前的 mismatch / 失败属于可恢复阻断,最终已履约成功,不需要人工处理; +// 3. 已有明确结论的 Token 不再算异常: +// - 已最终发货(status=redeemed 或 redeemed_at 非空):发货前的 mismatch / 失败属于可恢复 +// 阻断,最终已履约成功,不需要人工处理; +// - 已上报失效(status=abnormal):库存状态列已明确标识,无需重复标记; // 4. 只要命中任意一条即为「异常」,不改变 Token 主状态。 // // 采用子查询而不是落库字段,保证历史数据无需迁移即可被筛选到。 @@ -27,8 +29,9 @@ const rockAnomalyLogConditionSQL = `( OR l.reason LIKE '%不符%' )` -// rockAnomalyTokenScopeSQL 排除已最终发货的 Token(别名固定为 t)。 -const rockAnomalyTokenScopeSQL = `t.status <> 'redeemed' AND t.redeemed_at IS NULL` +// rockAnomalyTokenScopeSQL 排除已有明确结论的 Token(别名固定为 t): +// 已最终发货(redeemed 或 redeemed_at 非空)与已上报失效(abnormal)。 +const rockAnomalyTokenScopeSQL = `t.status NOT IN ('redeemed', 'abnormal') AND t.redeemed_at IS NULL` func rockTokenAnomalyExistsSQL() string { return `EXISTS ( diff --git a/backend/internal/service/rock_anomaly_marker_test.go b/backend/internal/service/rock_anomaly_marker_test.go index a1f8810..9695b19 100644 --- a/backend/internal/service/rock_anomaly_marker_test.go +++ b/backend/internal/service/rock_anomaly_marker_test.go @@ -261,3 +261,66 @@ func TestListTokens_AnomalyFilterExcludesDeliveredTokens(t *testing.T) { t.Fatalf("异常徽标应只统计未发货的 1 条,实际 %d", stats.AnomalyStock) } } + +// 已上报失效(abnormal)的 Token 状态列已明确标识,不再叠加异常标记。 +func TestListTokens_AnomalyFilterExcludesAbnormalTokens(t *testing.T) { + db := newRockTestDB(t) + svc := NewRockService(db, NewRockClient("https://example.com")) + if _, err := svc.GetConfig(context.Background()); err != nil { + t.Fatalf("初始化配置失败: %v", err) + } + + now := time.Now() + abnormalToken := model.RockToken{ + UpstreamTokenID: "ABNORMAL-1", + Token: "jwt-abnormal-1", + RedeemCode: "CODE-ABNORMAL-1", + Color: model.RockColorBlack, + Status: model.RockStatusAbnormal, + BoundAt: &now, + } + boundToken := model.RockToken{ + UpstreamTokenID: "BOUND-2", + Token: "jwt-bound-2", + RedeemCode: "CODE-BOUND-2", + Color: model.RockColorBlack, + Status: model.RockStatusBound, + BoundAt: &now, + } + + for _, token := range []*model.RockToken{&abnormalToken, &boundToken} { + if err := db.Create(token).Error; err != nil { + t.Fatalf("创建 Token 失败: %v", err) + } + if err := db.Create(&model.RockTokenStatusLog{ + TokenID: token.ID, + UpstreamTokenID: token.UpstreamTokenID, + EventType: rockTokenEventStatusChanged, + FromStatus: model.RockStatusBound, + ToStatus: model.RockStatusAbnormal, + Source: rockTokenSourceRefresh, + Reason: "上游卡密不存在(刷新自动同步)", + }).Error; err != nil { + t.Fatalf("创建状态日志失败: %v", err) + } + } + + list, total, err := svc.ListTokens(context.Background(), 1, 20, model.RockStatusAnomaly, "") + if err != nil { + t.Fatalf("查询异常列表失败: %v", err) + } + if total != 1 || len(list) != 1 || list[0].ID != boundToken.ID { + t.Fatalf("已上报失效 Token 不应命中异常筛选,实际 total=%d len=%d", total, len(list)) + } + + stats, err := svc.GetStats(context.Background()) + if err != nil { + t.Fatalf("获取统计失败: %v", err) + } + if stats.AnomalyStock != 1 { + t.Fatalf("异常徽标应只统计 1 条,实际 %d", stats.AnomalyStock) + } + if stats.AbnormalStock != 1 { + t.Fatalf("已上报失效库存应正常统计为 1,实际 %d", stats.AbnormalStock) + } +}