云端数据 增加违规检测日志

This commit is contained in:
chfei committed 2026-11-02 17:19:49 +08:00
1 parent 1fa91a9cfb
commit 207696d417
7 files changed
+173 -10

No files matched your search

@@ -120,6 +120,22 @@ internal static class IntegrationChecks
new() { GoodsId = first, Reasons = ignoredName }
]);
Check(result.Count == 1 && result[0].GoodsId == first && result[0].IsOffShelf, "batch report and deduplication");
var ignoredReasonId = (await db.Select<ViolationReasonEntity>().Where(x => x.Name == ignoredName).FirstAsync()).Id;
Check(!await db.Select<GoodsViolationLogEntity>().Where(x => x.GoodsId == first).AnyAsync(), "reporting alone creates no log");
await violations.RecordLogsAsync([
new() { ShopId = "shop-a", GoodsId = first, ReasonId = (int)existingReasonId, Action = ViolationLogAction.Report },
new() { ShopId = "shop-c", GoodsId = first, ReasonId = (int)ignoredReasonId, Action = ViolationLogAction.Report }
]);
var reportLogs = await violations.GetLogsAsync(["shop-a", "shop-c"], first, 0, 50);
Check(reportLogs.Count == 2 && reportLogs.All(x => x.Action == ViolationLogAction.Report)
&& reportLogs.Any(x => x.ShopId == "shop-a" && x.ReasonId == existingReasonId)
&& reportLogs.Any(x => x.ShopId == "shop-c" && x.ReasonId == ignoredReasonId), "keep report source and ignored reasons");
foreach (var action in new[] { ViolationLogAction.AutoListingExcluded, ViolationLogAction.AutoOffShelf })
await violations.RecordLogsAsync([new() { ShopId = "shop-b", GoodsId = first, ReasonId = (int)existingReasonId, Action = action }]);
var latest = (await violations.GetLogsAsync(["shop-b"], first, 0, 1)).Single();
var previous = (await violations.GetLogsAsync(["shop-b"], first, latest.Id, 1)).Single();
Check(latest.Action == ViolationLogAction.AutoOffShelf && previous.Action == ViolationLogAction.AutoListingExcluded,
"client actions are filtered by shop and paginated newest first");
Check(!await Exists(first), "report deletes real OpenSearch document");
reasons.Add((await db.Select<ViolationReasonEntity>().Where(x => x.Name == names[0]).FirstAsync()).Id);
reasons.Add((await db.Select<ViolationReasonEntity>().Where(x => x.Name == names[1]).FirstAsync()).Id);
@@ -165,6 +181,8 @@ internal static class IntegrationChecks
var status = await violations.GetStatusAsync([first, first + 1, first, first + 100]);
Check(status.Count == 3 && status[0].IsViolation && !status[1].IsViolation && !status[2].IsViolation, "batch status including missing goods");
Check(status[0].ReasonIds.ToHashSet().SetEquals(before.Select(id => (int)id))
&& status[1].ReasonIds.Length == 0 && status[2].ReasonIds.Length == 0, "status returns only current reasons for each product");
Check((await Reasons(first)).Length == 3, "scalar reasons stored in separate rows");
Check((await violations.GetGoodsIdsByReasonAsync(reasons[2], first - 1)).Contains(first), "scalar reason query");
try
@@ -310,6 +328,7 @@ internal static class IntegrationChecks
if (ownsIds)
{
await db.Delete<GoodsEntity>().Where(x => ids.Contains(x.GoodsId)).ExecuteAffrowsAsync();
await db.Delete<GoodsViolationLogEntity>().Where(x => ids.Contains(x.GoodsId)).ExecuteAffrowsAsync();
await db.Delete<GoodsViolationEntity>().Where(x => ids.Contains(x.GoodsId)).ExecuteAffrowsAsync();
}
await db.Delete<ViolationReasonEntity>().Where(x => x.Name == ignoredName).ExecuteAffrowsAsync();
+12
View File
@@ -81,6 +81,18 @@ try
catch (JsonException) { checks++; }
Check(!typeof(GoodsController).GetMethods().Any(x => x.Name.Contains("ByReason")), "Reason query must not be exposed");
var removalService = new GoodsViolationService(null!, null!, null!, NullLogger<GoodsViolationService>.Instance);
Check(JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.Web)).Contains("shopId") == false,
"report payload excludes log metadata");
Reject(() => removalService.RecordLogsAsync([new() { ShopId = "A", GoodsId = 1, ReasonId = 1, Action = (ViolationLogAction)4 }]).GetAwaiter().GetResult());
Reject(() => removalService.RecordLogsAsync([new() { GoodsId = 1, ReasonId = 1, Action = ViolationLogAction.AutoOffShelf }]).GetAwaiter().GetResult());
Reject(() => removalService.RecordLogsAsync([]).GetAwaiter().GetResult());
Reject(() => removalService.RecordLogsAsync(new RecordViolationLogRequest[1001]).GetAwaiter().GetResult());
Reject(() => removalService.RecordLogsAsync([new() { ShopId = "A", GoodsId = 1, ReasonId = 1, Action = ViolationLogAction.Report }, null!]).GetAwaiter().GetResult());
Reject(() => removalService.GetLogsAsync(["a"], 1, -1, 50).GetAwaiter().GetResult());
Reject(() => removalService.GetLogsAsync(["a"], null, 0, 201).GetAwaiter().GetResult());
Reject(() => removalService.GetLogsAsync([], null, 0, 50).GetAwaiter().GetResult());
Reject(() => removalService.GetLogsAsync(["a", " "], null, 0, 50).GetAwaiter().GetResult());
Reject(() => removalService.RecordLogsAsync([new() { ShopId = "A", GoodsId = 1, ReasonId = 0, Action = ViolationLogAction.AutoOffShelf }]).GetAwaiter().GetResult());
Reject(() => removalService.RemoveAsync(null).GetAwaiter().GetResult());
Reject(() => removalService.RemoveAsync([]).GetAwaiter().GetResult());
Reject(() => removalService.RemoveAsync([0]).GetAwaiter().GetResult());