feat: cache contacts and add forced sync
Build web service image / build (push) Successful in 1m13s

This commit is contained in:
2026-09-23 11:17:43 +08:00
parent cc2321aa86
commit bfa0059d3a
13 changed files with 708 additions and 121 deletions
+38 -5
View File
@@ -24,7 +24,21 @@ public sealed record RemoteSyncBatch(
[property: JsonPropertyName("payload_hash")] string PayloadHash,
[property: JsonPropertyName("coverage_state")] string CoverageState,
[property: JsonPropertyName("conversations")] IReadOnlyList<RemoteSyncConversation> Conversations,
[property: JsonPropertyName("messages")] IReadOnlyList<RemoteSyncMessage> Messages);
[property: JsonPropertyName("messages")] IReadOnlyList<RemoteSyncMessage> Messages)
{
[JsonPropertyName("contacts")]
public IReadOnlyList<RemoteSyncContact> Contacts { get; init; } = [];
[JsonPropertyName("contacts_snapshot_id")]
public string? ContactsSnapshotId { get; init; }
}
public sealed record RemoteSyncContact(
[property: JsonPropertyName("chat_id")] string ChatId,
[property: JsonPropertyName("chat_type")] ReportingChatType ChatType,
[property: JsonPropertyName("display_name")] string DisplayName,
[property: JsonPropertyName("remark")] string Remark,
[property: JsonPropertyName("observed_at")] DateTimeOffset ObservedAt);
public sealed record RemoteSyncConversation(
[property: JsonPropertyName("chat_id")] string ChatId,
@@ -276,14 +290,19 @@ public static class RemoteDataBatchAuthorization
ReportingAuthorization.Check(config, batch.AccountId, conversation.ChatId, conversation.ChatType, ReportingDataType.TaskResult).Allowed).ToArray();
var messages = batch.Messages.Where(message =>
ReportingAuthorization.Check(config, batch.AccountId, message.ChatId, message.ChatType, ReportingDataType.Message).Allowed).ToArray();
var dropped = conversations.Length != batch.Conversations.Count || messages.Length != batch.Messages.Count;
var contacts = batch.Contacts.Where(contact =>
ReportingAuthorization.Check(config, batch.AccountId, contact.ChatId, contact.ChatType, ReportingDataType.TaskResult).Allowed).ToArray();
var dropped = conversations.Length != batch.Conversations.Count || messages.Length != batch.Messages.Count || contacts.Length != batch.Contacts.Count;
reason = dropped ? "SomeRecordsDroppedByReporting" : "Authorized";
if (conversations.Length == 0 && messages.Length == 0 && (batch.Conversations.Count > 0 || batch.Messages.Count > 0))
if (conversations.Length == 0 && messages.Length == 0 && contacts.Length == 0 && (batch.Conversations.Count > 0 || batch.Messages.Count > 0 || batch.Contacts.Count > 0))
{
reason = "AllRecordsDroppedByReporting";
return null;
}
return dropped ? batch with { CoverageState = "partial", Conversations = conversations, Messages = messages } : batch;
if (!dropped)
return batch;
var filteredBatch = batch with { CoverageState = "partial", Conversations = conversations, Messages = messages, Contacts = contacts };
return filteredBatch with { PayloadHash = ComputePayloadHash(filteredBatch) };
}
}
@@ -440,7 +459,21 @@ public sealed class RemoteDataBatchQueue
{
foreach (var value in new[] { batch.NodeId, batch.AccountId, batch.BatchId, batch.SourceGeneration, batch.StreamKey, batch.PayloadHash })
RemoteAgentOptions.ValidateIdentifier(value, "sync batch field", 512);
if (batch.Sequence < 1 || batch.Conversations.Count + batch.Messages.Count > RemoteDataProtocol.MaxBatchMessages)
if (batch.Sequence < 1 || batch.Conversations.Count + batch.Messages.Count > RemoteDataProtocol.MaxBatchMessages
|| batch.Contacts.Count > RemoteDataProtocol.MaxBatchItems
|| batch.StreamKey is not ("messages" or "contacts")
|| batch.StreamKey == "contacts" && (string.IsNullOrWhiteSpace(batch.ContactsSnapshotId) || batch.Conversations.Count > 0 || batch.Messages.Count > 0)
|| batch.StreamKey == "messages" && (batch.Contacts.Count > 0 || batch.ContactsSnapshotId is not null))
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "The sync batch sequence or item count is invalid.");
if (batch.ContactsSnapshotId is not null)
RemoteAgentOptions.ValidateIdentifier(batch.ContactsSnapshotId, "contacts snapshot", 512);
foreach (var contact in batch.Contacts)
{
if (string.IsNullOrWhiteSpace(contact.ChatId) || contact.ChatId.Length > 512
|| contact.DisplayName.Length > 2048 || contact.Remark.Length > 2048
|| contact.ChatType is not (ReportingChatType.Group or ReportingChatType.Private)
|| contact.ObservedAt == default)
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "The contact record is invalid.");
}
}
}