🧱 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); .FirstOrDefaultAsync(a => a.AutomatedId == automatedId);
if (account == null) 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); var perk = await subscriptions.GetPerkSubscriptionAsync(account.Id);
account.PerkSubscription = perk?.ToReference(); account.PerkSubscription = perk?.ToReference();
@@ -159,7 +160,8 @@ public class AccountServiceGrpc(
return response; 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 var accounts = await _db.Accounts
.AsNoTracking() .AsNoTracking()
@@ -232,21 +234,48 @@ public class AccountServiceGrpc(
public override async Task<ListRelationshipSimpleResponse> ListFriends( public override async Task<ListRelationshipSimpleResponse> ListFriends(
ListRelationshipSimpleRequest request, ServerCallContext context) ListRelationshipSimpleRequest request, ServerCallContext context)
{ {
var resp = new ListRelationshipSimpleResponse();
switch (request.RelationIdentifierCase)
{
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.AccountId:
var accountId = Guid.Parse(request.AccountId); var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountFriends(accountId); var relationship = await relationships.ListAccountFriends(accountId);
var resp = new ListRelationshipSimpleResponse();
resp.AccountsId.AddRange(relationship.Select(x => x.ToString())); resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp; 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( public override async Task<ListRelationshipSimpleResponse> ListBlocked(
ListRelationshipSimpleRequest request, ServerCallContext context) ListRelationshipSimpleRequest request, ServerCallContext context)
{ {
var resp = new ListRelationshipSimpleResponse();
switch (request.RelationIdentifierCase)
{
case ListRelationshipSimpleRequest.RelationIdentifierOneofCase.AccountId:
var accountId = Guid.Parse(request.AccountId); var accountId = Guid.Parse(request.AccountId);
var relationship = await relationships.ListAccountBlocked(accountId); var relationship = await relationships.ListAccountBlocked(accountId);
var resp = new ListRelationshipSimpleResponse();
resp.AccountsId.AddRange(relationship.Select(x => x.ToString())); resp.AccountsId.AddRange(relationship.Select(x => x.ToString()));
return resp; 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, public override async Task<GetRelationshipResponse> GetRelationship(GetRelationshipRequest request,

View File

@@ -52,7 +52,8 @@ public class RelationshipService(
return relationship; 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) if (status == RelationshipStatus.Pending)
throw new InvalidOperationException( throw new InvalidOperationException(
@@ -169,12 +170,14 @@ public class RelationshipService(
await db.SaveChangesAsync(); await db.SaveChangesAsync();
await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId, RelationshipStatus.Friends, status); await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId, RelationshipStatus.Friends,
status);
return relationshipBackward; 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); var relationship = await GetRelationship(accountId, relatedId);
if (relationship is null) throw new ArgumentException("There is no relationship between you and the user."); 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; 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, public async Task<bool> HasRelationshipWithStatus(Guid accountId, Guid relatedId,
@@ -216,29 +221,26 @@ public class RelationshipService(
return relationship is not null; 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) if (accountId == Guid.Empty)
throw new ArgumentException("Account ID cannot be 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); var relationships = await cache.GetAsync<List<Guid>>(cacheKey);
if (relationships != null) return relationships; if (relationships != null) return relationships;
var now = Instant.FromDateTimeUtc(DateTime.UtcNow); var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
var query = db.AccountRelationships 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.Status == status)
.Where(r => r.ExpiredAt == null || r.ExpiredAt > now) .Where(r => r.ExpiredAt == null || r.ExpiredAt > now)
.Select(r => r.AccountId); .Select(r => isRelated ? r.AccountId : r.RelatedId);
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);
}
relationships = await query.ToListAsync(); relationships = await query.ToListAsync();

View File

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