Action log service grpc

This commit is contained in:
2025-07-14 22:36:59 +08:00
parent cbfdb4aa60
commit a03b8d1cac
7 changed files with 191 additions and 274 deletions

View File

@@ -56,68 +56,6 @@ public class AccountServiceGrpc(
return response;
}
public override async Task<Shared.Proto.Account> CreateAccount(CreateAccountRequest request,
ServerCallContext context)
{
// Map protobuf request to domain model
var account = new Account
{
Name = request.Name,
Nick = request.Nick,
Language = request.Language,
IsSuperuser = request.IsSuperuser,
ActivatedAt = request.Profile != null ? null : _clock.GetCurrentInstant(),
Profile = new AccountProfile
{
FirstName = request.Profile?.FirstName,
LastName = request.Profile?.LastName,
// Initialize other profile fields as needed
}
};
// Add to database
_db.Accounts.Add(account);
await _db.SaveChangesAsync();
_logger.LogInformation("Created new account with ID {AccountId}", account.Id);
return account.ToProtoValue();
}
public override async Task<Shared.Proto.Account> UpdateAccount(UpdateAccountRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.Id, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var account = await _db.Accounts.FindAsync(accountId);
if (account == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, $"Account {request.Id} not found"));
// Update fields if they are provided in the request
if (request.Name != null) account.Name = request.Name;
if (request.Nick != null) account.Nick = request.Nick;
if (request.Language != null) account.Language = request.Language;
if (request.IsSuperuser != null) account.IsSuperuser = request.IsSuperuser.Value;
await _db.SaveChangesAsync();
return account.ToProtoValue();
}
public override async Task<Empty> DeleteAccount(DeleteAccountRequest request, ServerCallContext context)
{
if (!Guid.TryParse(request.Id, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var account = await _db.Accounts.FindAsync(accountId);
if (account == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, $"Account {request.Id} not found"));
_db.Accounts.Remove(account);
await _db.SaveChangesAsync();
return new Empty();
}
public override async Task<ListAccountsResponse> ListAccounts(ListAccountsRequest request,
ServerCallContext context)
{
@@ -161,210 +99,6 @@ public class AccountServiceGrpc(
return response;
}
// Implement other service methods following the same pattern...
// Profile operations
public override async Task<Shared.Proto.AccountProfile> GetProfile(GetProfileRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var profile = await _db.AccountProfiles
.AsNoTracking()
.FirstOrDefaultAsync(p => p.AccountId == accountId);
if (profile == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
$"Profile for account {request.AccountId} not found"));
return profile.ToProtoValue();
}
public override async Task<Shared.Proto.AccountProfile> UpdateProfile(UpdateProfileRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var profile = await _db.AccountProfiles
.FirstOrDefaultAsync(p => p.AccountId == accountId);
if (profile == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
$"Profile for account {request.AccountId} not found"));
// Update only the fields specified in the field mask
if (request.UpdateMask == null || request.UpdateMask.Paths.Contains("first_name"))
profile.FirstName = request.Profile.FirstName;
if (request.UpdateMask == null || request.UpdateMask.Paths.Contains("last_name"))
profile.LastName = request.Profile.LastName;
// Update other fields similarly...
await _db.SaveChangesAsync();
return profile.ToProtoValue();
}
// Contact operations
public override async Task<Shared.Proto.AccountContact> AddContact(AddContactRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var contact = new AccountContact
{
AccountId = accountId,
Type = (AccountContactType)request.Type,
Content = request.Content,
IsPrimary = request.IsPrimary,
VerifiedAt = null
};
_db.AccountContacts.Add(contact);
await _db.SaveChangesAsync();
return contact.ToProtoValue();
}
public override async Task<Empty> RemoveContact(RemoveContactRequest request, ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
if (!Guid.TryParse(request.Id, out var contactId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid contact ID format"));
var contact = await _db.AccountContacts.FirstOrDefaultAsync(c => c.Id == contactId && c.AccountId == accountId);
if (contact == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Contact not found."));
_db.AccountContacts.Remove(contact);
await _db.SaveChangesAsync();
return new Empty();
}
public override async Task<ListContactsResponse> ListContacts(ListContactsRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var query = _db.AccountContacts.AsNoTracking().Where(c => c.AccountId == accountId);
if (request.VerifiedOnly)
query = query.Where(c => c.VerifiedAt != null);
var contacts = await query.ToListAsync();
var response = new ListContactsResponse();
response.Contacts.AddRange(contacts.Select(c => c.ToProtoValue()));
return response;
}
public override async Task<Shared.Proto.AccountContact> VerifyContact(VerifyContactRequest request,
ServerCallContext context)
{
// This is a placeholder implementation. In a real-world scenario, you would
// have a more robust verification mechanism (e.g., sending a code to the
// user's email or phone).
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
if (!Guid.TryParse(request.Id, out var contactId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid contact ID format"));
var contact = await _db.AccountContacts.FirstOrDefaultAsync(c => c.Id == contactId && c.AccountId == accountId);
if (contact == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Contact not found."));
contact.VerifiedAt = _clock.GetCurrentInstant();
await _db.SaveChangesAsync();
return contact.ToProtoValue();
}
// Badge operations
public override async Task<Shared.Proto.AccountBadge> AddBadge(AddBadgeRequest request, ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var badge = new AccountBadge
{
AccountId = accountId,
Type = request.Type,
Label = request.Label,
Caption = request.Caption,
ActivatedAt = _clock.GetCurrentInstant(),
ExpiredAt = request.ExpiredAt?.ToInstant(),
Meta = request.Meta.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value)
};
_db.Badges.Add(badge);
await _db.SaveChangesAsync();
return badge.ToProtoValue();
}
public override async Task<Empty> RemoveBadge(RemoveBadgeRequest request, ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
if (!Guid.TryParse(request.Id, out var badgeId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid badge ID format"));
var badge = await _db.Badges.FirstOrDefaultAsync(b => b.Id == badgeId && b.AccountId == accountId);
if (badge == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Badge not found."));
_db.Badges.Remove(badge);
await _db.SaveChangesAsync();
return new Empty();
}
public override async Task<ListBadgesResponse> ListBadges(ListBadgesRequest request, ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var query = _db.Badges.AsNoTracking().Where(b => b.AccountId == accountId);
if (request.ActiveOnly)
query = query.Where(b => b.ExpiredAt == null || b.ExpiredAt > _clock.GetCurrentInstant());
var badges = await query.ToListAsync();
var response = new ListBadgesResponse();
response.Badges.AddRange(badges.Select(b => b.ToProtoValue()));
return response;
}
public override async Task<Shared.Proto.AccountProfile> SetActiveBadge(SetActiveBadgeRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var profile = await _db.AccountProfiles.FirstOrDefaultAsync(p => p.AccountId == accountId);
if (profile == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Profile not found."));
if (!string.IsNullOrEmpty(request.BadgeId) && !Guid.TryParse(request.BadgeId, out var badgeId))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid badge ID format"));
await _db.SaveChangesAsync();
return profile.ToProtoValue();
}
public override async Task<ListUserRelationshipSimpleResponse> ListFriends(
ListUserRelationshipSimpleRequest request, ServerCallContext context)
{