Added new fixture user blocked by user.

This commit is contained in:
Ruberoid 2025-07-21 16:38:22 +03:00
parent 1170ebdf07
commit 5f99ea15e0
13 changed files with 218 additions and 1 deletions

View File

@ -9,6 +9,11 @@ public sealed class UserData
/// </summary> /// </summary>
public required string Username { get; set; } public required string Username { get; set; }
/// <summary>
/// Заблокирован ли бот пользователем
/// </summary>
public bool BotBlocked { get; set; }
/// <summary> /// <summary>
/// Список идентити пользователя /// Список идентити пользователя
/// </summary> /// </summary>

View File

@ -15,4 +15,10 @@ public interface IUsersController
[Get(WebRoutes.Users.ByIdentity)] [Get(WebRoutes.Users.ByIdentity)]
Task<UserData?> GetByIdentity([Query] UserIdentityType identityType, [Query] string identity, CancellationToken cancellationToken = default); Task<UserData?> GetByIdentity([Query] UserIdentityType identityType, [Query] string identity, CancellationToken cancellationToken = default);
[Post(WebRoutes.Users.BlockBot)]
Task BlockBot([Path] long id, CancellationToken cancellationToken = default);
[Post(WebRoutes.Users.UnblockBot)]
Task UnblockBot([Path] long id, CancellationToken cancellationToken = default);
} }

View File

@ -11,5 +11,9 @@ public static class WebRoutes
public const string ById = "{id}"; public const string ById = "{id}";
public const string ByIdentity = "identity"; public const string ByIdentity = "identity";
public const string BlockBot = "{id}/block-bot";
public const string UnblockBot = "{id}/unblock-bot";
} }
} }

View File

@ -9,4 +9,6 @@ public interface IUsersRepository
Task<User?> GetUserById(long id, CancellationToken cancellationToken = default); Task<User?> GetUserById(long id, CancellationToken cancellationToken = default);
Task<User?> GetByIdentity(UserIdentityType identityType, string identity, CancellationToken cancellationToken = default); Task<User?> GetByIdentity(UserIdentityType identityType, string identity, CancellationToken cancellationToken = default);
Task UpdateBotBlockedStatus(long userId, bool botBlocked, CancellationToken cancellationToken = default);
} }

View File

@ -11,4 +11,8 @@ public interface IUsersService
Task<UserData?> GetById(long id, CancellationToken cancellationToken = default); Task<UserData?> GetById(long id, CancellationToken cancellationToken = default);
Task<UserData?> GetByIdentity(UserIdentityType identityType, string identity, CancellationToken cancellationToken = default); Task<UserData?> GetByIdentity(UserIdentityType identityType, string identity, CancellationToken cancellationToken = default);
Task BlockBot(long userId, CancellationToken cancellationToken = default);
Task UnblockBot(long userId, CancellationToken cancellationToken = default);
} }

View File

@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using Nocr.Users.Api.Contracts.Users; using Nocr.Users.Api.Contracts.Users;
using Nocr.Users.Api.Contracts.Users.Dto; using Nocr.Users.Api.Contracts.Users.Dto;
using Nocr.Users.AppServices.Users.Repositories; using Nocr.Users.AppServices.Users.Repositories;
@ -7,10 +8,12 @@ namespace Nocr.Users.AppServices.Users.Services;
public sealed class UsersService : IUsersService public sealed class UsersService : IUsersService
{ {
private readonly IUsersRepository _repository; private readonly IUsersRepository _repository;
private readonly ILogger<UsersService> _logger;
public UsersService(IUsersRepository repository) public UsersService(IUsersRepository repository, ILogger<UsersService> logger)
{ {
_repository = repository ?? throw new ArgumentNullException(nameof(repository)); _repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
} }
public async Task<long> Create(string username, string? telegramUsername, string? email, long? telegramId, public async Task<long> Create(string username, string? telegramUsername, string? email, long? telegramId,
@ -48,6 +51,32 @@ public sealed class UsersService : IUsersService
return MapToUserData(user); return MapToUserData(user);
} }
public async Task BlockBot(long userId, CancellationToken cancellationToken = default)
{
var user = await _repository.GetUserById(userId, cancellationToken);
if (user == null)
{
_logger.LogWarning("User with ID {UserId} not found for blocking bot", userId);
return;
}
await _repository.UpdateBotBlockedStatus(userId, true, cancellationToken);
_logger.LogInformation("Bot blocked for user {UserId}", userId);
}
public async Task UnblockBot(long userId, CancellationToken cancellationToken = default)
{
var user = await _repository.GetUserById(userId, cancellationToken);
if (user == null)
{
_logger.LogWarning("User with ID {UserId} not found for unblocking bot", userId);
return;
}
await _repository.UpdateBotBlockedStatus(userId, false, cancellationToken);
_logger.LogInformation("Bot unblocked for user {UserId}", userId);
}
private static UserData? MapToUserData(User? user) private static UserData? MapToUserData(User? user)
{ {
if (user == null) if (user == null)
@ -57,6 +86,7 @@ public sealed class UsersService : IUsersService
{ {
Id = user.Id, Id = user.Id,
Username = user.Username, Username = user.Username,
BotBlocked = user.BotBlocked,
Identities = user.Identities.Select(MapToUserIdentityData).ToArray() Identities = user.Identities.Select(MapToUserIdentityData).ToArray()
}; };
} }

View File

@ -5,6 +5,8 @@ public sealed class User
public long Id { get; set; } public long Id { get; set; }
public string Username { get; private set; } = default!; public string Username { get; private set; } = default!;
public bool BotBlocked { get; private set; }
private readonly List<UserIdentity> _identities = new(); private readonly List<UserIdentity> _identities = new();
@ -44,4 +46,14 @@ public sealed class User
return new User(username, identities); return new User(username, identities);
} }
public void MarkAsBlocked()
{
BotBlocked = true;
}
public void MarkAsUnblocked()
{
BotBlocked = false;
}
} }

View File

@ -37,4 +37,16 @@ public class UsersController : ControllerBase
{ {
return _usersService.GetByIdentity(identityType, identity, cancellationToken); return _usersService.GetByIdentity(identityType, identity, cancellationToken);
} }
[HttpPost(WebRoutes.Users.BlockBot)]
public Task BlockBot([FromRoute] long id, CancellationToken cancellationToken = default)
{
return _usersService.BlockBot(id, cancellationToken);
}
[HttpPost(WebRoutes.Users.UnblockBot)]
public Task UnblockBot([FromRoute] long id, CancellationToken cancellationToken = default)
{
return _usersService.UnblockBot(id, cancellationToken);
}
} }

View File

@ -0,0 +1,98 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Nocr.Users.Persistence;
#nullable disable
namespace Nocr.Users.Migrator.Migrations
{
[DbContext(typeof(UsersContext))]
[Migration("20250721124257_AddBotBlockedField")]
partial class AddBotBlockedField
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("Nocr.Users.AppServices.Users.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
b.Property<bool>("BotBlocked")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false);
b.Property<string>("Username")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("Username")
.IsUnique()
.HasDatabaseName("UX_users_username");
b.ToTable("Users");
});
modelBuilder.Entity("Nocr.Users.AppServices.Users.UserIdentity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
b.Property<string>("Identity")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("Identity", "Type")
.IsUnique()
.HasDatabaseName("UX_users_identity_identity_type");
b.ToTable("UserIdentity");
});
modelBuilder.Entity("Nocr.Users.AppServices.Users.UserIdentity", b =>
{
b.HasOne("Nocr.Users.AppServices.Users.User", null)
.WithMany("Identities")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.NoAction)
.IsRequired();
});
modelBuilder.Entity("Nocr.Users.AppServices.Users.User", b =>
{
b.Navigation("Identities");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Nocr.Users.Migrator.Migrations
{
/// <inheritdoc />
public partial class AddBotBlockedField : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "BotBlocked",
table: "Users",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BotBlocked",
table: "Users");
}
}
}

View File

@ -29,6 +29,11 @@ namespace Nocr.Users.Migrator.Migrations
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id")); MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
b.Property<bool>("BotBlocked")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false);
b.Property<string>("Username") b.Property<string>("Username")
.IsRequired() .IsRequired()
.HasColumnType("varchar(255)"); .HasColumnType("varchar(255)");

View File

@ -11,6 +11,9 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
builder.HasKey(x => x.Id); builder.HasKey(x => x.Id);
builder.Property(x => x.Username) builder.Property(x => x.Username)
.IsRequired(); .IsRequired();
builder.Property(x => x.BotBlocked)
.HasDefaultValue(false);
builder.HasIndex(x => x.Username) builder.HasIndex(x => x.Username)
.IsUnique() .IsUnique()

View File

@ -35,4 +35,11 @@ public class UsersRepository : IUsersRepository
.FirstOrDefaultAsync(x => x.Identities.Any(i => i.Type == identityType && i.Identity == identity), .FirstOrDefaultAsync(x => x.Identities.Any(i => i.Type == identityType && i.Identity == identity),
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
} }
public async Task UpdateBotBlockedStatus(long userId, bool botBlocked, CancellationToken cancellationToken = default)
{
await _db.Users
.Where(u => u.Id == userId)
.ExecuteUpdateAsync(u => u.SetProperty(x => x.BotBlocked, botBlocked), cancellationToken);
}
} }