🧱 Relationship query supports isRelated

This commit is contained in:
2026-01-01 13:50:55 +08:00
parent 7c5c92a501
commit 8c19bd6a73
3 changed files with 71 additions and 39 deletions

View File

@@ -53,7 +53,8 @@ public class AccountServiceGrpc(
.FirstOrDefaultAsync(a => a.AutomatedId == automatedId);
if (account == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, $"Account with automated ID {request.AutomatedId} not found"));
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
$"Account with automated ID {request.AutomatedId} not found"));
var perk = await subscriptions.GetPerkSubscriptionAsync(account.Id);
account.PerkSubscription = perk?.ToReference();
@@ -159,7 +160,8 @@ public class AccountServiceGrpc(
return response;
}
public override async Task<GetAccountBatchResponse> SearchAccount(SearchAccountRequest request, ServerCallContext context)
public override async Task<GetAccountBatchResponse> SearchAccount(SearchAccountRequest request,
ServerCallContext context)
{
var accounts = await _db.Accounts
.AsNoTracking()
@@ -232,21 +234,48 @@ public class AccountServiceGrpc(
public override async Task<ListRelationshipSimpleResponse> ListFriends(
ListRelationshipSimpleRequest request, ServerCallContext context)
{
var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountFriends(accountId);
var resp = new ListRelationshipSimpleResponse();
resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp;
switch (request.RelationIdentifierCase)
{
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.AccountId:
var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountFriends(accountId);
resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp;
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.RelatedId:
var relatedId = Guid.Parse(request.RelatedId);
var relatedRelationship = await relationships.ListAccountFriends(relatedId, true);
resp.AccountsId.AddRange(relatedRelationship.Select(x => x.ToString()));
return resp;
break;
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.None:
default:
throw new RpcException(new Status(StatusCode.InvalidArgument,
$"The relationship identifier must be provided."));
}
}
public override async Task<ListRelationshipSimpleResponse> ListBlocked(
ListRelationshipSimpleRequest request, ServerCallContext context)
{
var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountBlocked(accountId);
var resp = new ListRelationshipSimpleResponse();
resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp;
switch (request.RelationIdentifierCase)
{
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.AccountId:
var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountBlocked(accountId);
resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp;
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.RelatedId:
var relatedId = Guid.Parse(request.RelatedId);
var relatedRelationship = await relationships.ListAccountBlocked(relatedId, true);
resp.AccountsId.AddRange(relatedRelationship.Select(x => x.ToString()));
return resp;
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.None:
default:
throw new RpcException(new Status(StatusCode.InvalidArgument,
$"The relationship identifier must be provided."));
}
}
public override async Task<GetRelationshipResponse> GetRelationship(GetRelationshipRequest request,

View File

@@ -52,7 +52,8 @@ public class RelationshipService(
return relationship;
}
public async Task<SnAccountRelationship> CreateRelationship(SnAccount sender, SnAccount target, RelationshipStatus status)
public async Task<SnAccountRelationship> CreateRelationship(SnAccount sender, SnAccount target,
RelationshipStatus status)
{
if (status == RelationshipStatus.Pending)
throw new InvalidOperationException(
@@ -169,12 +170,14 @@ public class RelationshipService(
await db.SaveChangesAsync();
await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId, RelationshipStatus.Friends, status);
await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId, RelationshipStatus.Friends,
status);
return relationshipBackward;
}
public async Task<SnAccountRelationship> UpdateRelationship(Guid accountId, Guid relatedId, RelationshipStatus status)
public async Task<SnAccountRelationship> UpdateRelationship(Guid accountId, Guid relatedId,
RelationshipStatus status)
{
var relationship = await GetRelationship(accountId, relatedId);
if (relationship is null) throw new ArgumentException("There is no relationship between you and the user.");
@@ -189,24 +192,26 @@ public class RelationshipService(
return relationship;
}
public async Task<List<Guid>> ListAccountFriends(SnAccount account)
public async Task<List<Guid>> ListAccountFriends(SnAccount account, bool isRelated = false)
{
return await ListAccountFriends(account.Id);
return await ListAccountFriends(account.Id, isRelated);
}
public async Task<List<Guid>> ListAccountFriends(Guid accountId)
public async Task<List<Guid>> ListAccountFriends(Guid accountId, bool isRelated = false)
{
return await GetCachedRelationships(accountId, RelationshipStatus.Friends, UserFriendsCacheKeyPrefix);
return await GetCachedRelationships(accountId, RelationshipStatus.Friends, UserFriendsCacheKeyPrefix,
isRelated);
}
public async Task<List<Guid>> ListAccountBlocked(SnAccount account)
public async Task<List<Guid>> ListAccountBlocked(SnAccount account, bool isRelated = false)
{
return await ListAccountBlocked(account.Id);
return await ListAccountBlocked(account.Id, isRelated);
}
public async Task<List<Guid>> ListAccountBlocked(Guid accountId)
public async Task<List<Guid>> ListAccountBlocked(Guid accountId, bool isRelated = false)
{
return await GetCachedRelationships(accountId, RelationshipStatus.Blocked, UserBlockedCacheKeyPrefix);
return await GetCachedRelationships(accountId, RelationshipStatus.Blocked, UserBlockedCacheKeyPrefix,
isRelated);
}
public async Task<bool> HasRelationshipWithStatus(Guid accountId, Guid relatedId,
@@ -216,29 +221,26 @@ public class RelationshipService(
return relationship is not null;
}
private async Task<List<Guid>> GetCachedRelationships(Guid accountId, RelationshipStatus status, string cachePrefix)
private async Task<List<Guid>> GetCachedRelationships(
Guid accountId,
RelationshipStatus status,
string cachePrefix,
bool isRelated = false
)
{
if (accountId == Guid.Empty)
throw new ArgumentException("Account ID cannot be empty.");
var cacheKey = $"{cachePrefix}{accountId}";
var cacheKey = $"{cachePrefix}{accountId}:{isRelated}";
var relationships = await cache.GetAsync<List<Guid>>(cacheKey);
if (relationships != null) return relationships;
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
var query = db.AccountRelationships
.Where(r => r.RelatedId == accountId)
.Where(r => isRelated ? r.RelatedId == accountId : r.AccountId == accountId)
.Where(r => r.Status == status)
.Where(r => r.ExpiredAt == null || r.ExpiredAt > now)
.Select(r => r.AccountId);
if (status == RelationshipStatus.Friends)
{
var usersBlockedByMe = db.AccountRelationships
.Where(r => r.AccountId == accountId && r.Status == RelationshipStatus.Blocked)
.Select(r => r.RelatedId);
query = query.Except(usersBlockedByMe);
}
.Select(r => isRelated ? r.AccountId : r.RelatedId);
relationships = await query.ToListAsync();

View File

@@ -290,8 +290,6 @@ service AccountService {
rpc ListConnections(ListConnectionsRequest) returns (ListConnectionsResponse) {}
// Relationship Operations
rpc ListRelationships(ListRelationshipsRequest) returns (ListRelationshipsResponse) {}
rpc GetRelationship(GetRelationshipRequest) returns (GetRelationshipResponse) {}
rpc HasRelationship(GetRelationshipRequest) returns (google.protobuf.BoolValue) {}
rpc ListFriends(ListRelationshipSimpleRequest) returns (ListRelationshipSimpleResponse) {}
@@ -489,7 +487,10 @@ message GetRelationshipResponse {
}
message ListRelationshipSimpleRequest {
string account_id = 1;
oneof relation_identifier {
string account_id = 1;
string related_id = 2;
}
}
message ListRelationshipSimpleResponse {